/*      */ package com.zimbra.cs.mailbox;
/*      */ 
/*      */ import com.google.common.base.Strings;
/*      */ import com.zimbra.common.account.Key.AccountBy;
/*      */ import com.zimbra.common.calendar.ICalTimeZone;
/*      */ import com.zimbra.common.calendar.ParsedDateTime;
/*      */ import com.zimbra.common.calendar.ParsedDuration;
/*      */ import com.zimbra.common.calendar.TimeZoneMap;
/*      */ import com.zimbra.common.calendar.ZCalendar.ICalTok;
/*      */ import com.zimbra.common.calendar.ZCalendar.ZProperty;
/*      */ import com.zimbra.common.calendar.ZCalendar.ZVCalendar;
/*      */ import com.zimbra.common.localconfig.DebugConfig;
/*      */ import com.zimbra.common.mailbox.Color;
/*      */ import com.zimbra.common.mime.shim.JavaMailInternetAddress;
/*      */ import com.zimbra.common.service.ServiceException;
/*      */ import com.zimbra.common.util.ByteUtil;
/*      */ import com.zimbra.common.util.Log;
/*      */ import com.zimbra.common.util.LogFactory;
/*      */ import com.zimbra.common.util.Pair;
/*      */ import com.zimbra.common.util.ZimbraLog;
/*      */ import com.zimbra.common.zmime.ZMimeBodyPart;
/*      */ import com.zimbra.common.zmime.ZMimeMultipart;
/*      */ import com.zimbra.cs.account.Account;
/*      */ import com.zimbra.cs.account.CalendarResource;
/*      */ import com.zimbra.cs.account.Provisioning;
/*      */ import com.zimbra.cs.db.DbMailItem;
/*      */ import com.zimbra.cs.index.IndexDocument;
/*      */ import com.zimbra.cs.index.analysis.FieldTokenStream;
/*      */ import com.zimbra.cs.index.analysis.RFC822AddressTokenStream;
/*      */ import com.zimbra.cs.mailbox.calendar.Alarm;
/*      */ import com.zimbra.cs.mailbox.calendar.Alarm.Action;
/*      */ import com.zimbra.cs.mailbox.calendar.CalendarMailSender;
/*      */ import com.zimbra.cs.mailbox.calendar.IcalXmlStrMap;
/*      */ import com.zimbra.cs.mailbox.calendar.Invite;
/*      */ import com.zimbra.cs.mailbox.calendar.InviteChanges;
/*      */ import com.zimbra.cs.mailbox.calendar.InviteInfo;
/*      */ import com.zimbra.cs.mailbox.calendar.RecurId;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence.CancellationRule;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence.ExceptionRule;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence.RecurrenceRule;
/*      */ import com.zimbra.cs.mailbox.calendar.Recurrence.SimpleRepeatingRule;
/*      */ import com.zimbra.cs.mailbox.calendar.Util;
/*      */ import com.zimbra.cs.mailbox.calendar.ZAttendee;
/*      */ import com.zimbra.cs.mailbox.calendar.ZOrganizer;
/*      */ import com.zimbra.cs.mailbox.calendar.ZRecur;
/*      */ import com.zimbra.cs.mailbox.calendar.ZRecur.Frequency;
/*      */ import com.zimbra.cs.mime.Mime;
/*      */ import com.zimbra.cs.mime.Mime.FixedMimeMessage;
/*      */ import com.zimbra.cs.mime.ParsedAddress;
/*      */ import com.zimbra.cs.mime.ParsedMessage;
/*      */ import com.zimbra.cs.mime.ParsedMessage.CalendarPartInfo;
/*      */ import com.zimbra.cs.service.mail.CalendarUtils;
/*      */ import com.zimbra.cs.store.MailboxBlob;
/*      */ import com.zimbra.cs.store.StagedBlob;
/*      */ import com.zimbra.cs.store.StoreManager;
/*      */ import com.zimbra.cs.util.AccountUtil.AccountAddressMatcher;
/*      */ import com.zimbra.cs.util.JMSession;
/*      */ import com.zimbra.soap.mail.type.CalendarReply;
/*      */ import java.io.ByteArrayInputStream;
/*      */ import java.io.IOException;
/*      */ import java.io.InputStream;
/*      */ import java.io.OutputStream;
/*      */ import java.text.ParseException;
/*      */ import java.util.ArrayList;
/*      */ import java.util.Collection;
/*      */ import java.util.Collections;
/*      */ import java.util.Comparator;
/*      */ import java.util.Date;
/*      */ import java.util.GregorianCalendar;
/*      */ import java.util.HashMap;
/*      */ import java.util.HashSet;
/*      */ import java.util.Iterator;
/*      */ import java.util.List;
/*      */ import java.util.Map;
/*      */ import java.util.Set;
/*      */ import javax.activation.DataHandler;
/*      */ import javax.activation.DataSource;
/*      */ import javax.mail.MessagingException;
/*      */ import javax.mail.internet.AddressException;
/*      */ import javax.mail.internet.ContentType;
/*      */ import javax.mail.internet.InternetAddress;
/*      */ import javax.mail.internet.MimeBodyPart;
/*      */ import javax.mail.internet.MimeMessage;
/*      */ import javax.mail.internet.MimeMultipart;
/*      */ import javax.mail.util.SharedByteArrayInputStream;
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ public abstract class CalendarItem
/*      */   extends MailItem
/*      */ {
/*      */   public static final String INDEX_FIELD_ITEM_CLASS = "_calendaritemclass";
/*      */   public static final long NEXT_ALARM_KEEP_CURRENT = 0L;
/*      */   public static final long NEXT_ALARM_ALL_DISMISSED = -1L;
/*      */   public static final long NEXT_ALARM_FROM_NOW = -2L;
/*  133 */   static Log sLog = LogFactory.getLog(CalendarItem.class);
/*      */   
/*      */ 
/*      */   private String mUid;
/*      */   
/*      */ 
/*      */   private long mStartTime;
/*      */   
/*      */   private long mEndTime;
/*      */   
/*      */   private AlarmData mAlarmData;
/*      */   private Recurrence.IRecurrence mRecurrence;
/*      */   private TimeZoneMap mTzMap;
/*      */   private List<Invite> mInvites;
/*      */   private ReplyList mReplyList;
/*      */   public static final String FN_CALITEM_RECURRENCE = "apptRecur";
/*      */   
/*  150 */   protected ReplyList getReplyList() { return this.mReplyList; }
/*      */   
/*  152 */   public TimeZoneMap getTimeZoneMap() { return this.mTzMap; }
/*      */   
/*      */   public static class CalendarMetadata {
/*      */     public int mailboxId;
/*      */     public int itemId;
/*      */     public String uid;
/*      */     public int mod_metadata;
/*      */     public int mod_content;
/*      */     public long start_time;
/*      */     public long end_time;
/*      */     
/*      */     public CalendarMetadata(int mailboxId, int itemId, String uid, int mod_metadata, int mod_content, long start_time, long end_time) {
/*  164 */       this.mailboxId = mailboxId;
/*  165 */       this.itemId = itemId;
/*  166 */       this.uid = uid;
/*  167 */       this.mod_metadata = mod_metadata;
/*  168 */       this.mod_content = mod_content;
/*  169 */       this.start_time = start_time;
/*  170 */       this.end_time = end_time;
/*      */     }
/*      */   }
/*      */   
/*      */   protected CalendarItem(Mailbox mbox, MailItem.UnderlyingData data) throws ServiceException {
/*  175 */     this(mbox, data, false);
/*      */   }
/*      */   
/*      */   protected CalendarItem(Mailbox mbox, MailItem.UnderlyingData data, boolean skipCache) throws ServiceException {
/*  179 */     super(mbox, data, skipCache);
/*  180 */     if ((this.mData.type != MailItem.Type.APPOINTMENT.toByte()) && (this.mData.type != MailItem.Type.TASK.toByte())) {
/*  181 */       throw new IllegalArgumentException();
/*      */     }
/*      */   }
/*      */   
/*      */   public Recurrence.IRecurrence getRecurrence() {
/*  186 */     return this.mRecurrence;
/*      */   }
/*      */   
/*      */   public boolean isRecurring() {
/*  190 */     return this.mRecurrence != null;
/*      */   }
/*      */   
/*      */   public boolean hasExceptions() {
/*  194 */     if (isRecurring()) {
/*  195 */       for (Invite inv : this.mInvites) {
/*  196 */         if (inv.hasRecurId()) {
/*  197 */           return true;
/*      */         }
/*      */       }
/*      */     }
/*  201 */     return false;
/*      */   }
/*      */   
/*      */   public String getSender()
/*      */   {
/*  206 */     String sender = null;
/*  207 */     Invite firstInvite = getDefaultInviteOrNull();
/*  208 */     if (firstInvite != null) {
/*  209 */       ZOrganizer org = firstInvite.getOrganizer();
/*  210 */       if (org != null)
/*  211 */         sender = org.getIndexString();
/*      */     }
/*  213 */     return Strings.nullToEmpty(sender);
/*      */   }
/*      */   
/*      */   public ZOrganizer getOrganizer() {
/*  217 */     Invite firstInvite = getDefaultInviteOrNull();
/*  218 */     if (firstInvite == null) {
/*  219 */       return null;
/*      */     }
/*  221 */     return firstInvite.getOrganizer();
/*      */   }
/*      */   
/*      */   public Account getOrganizerAccount() throws ServiceException {
/*  225 */     Invite firstInvite = getDefaultInviteOrNull();
/*  226 */     if (firstInvite == null) {
/*  227 */       return null;
/*      */     }
/*  229 */     return firstInvite.getOrganizerAccount();
/*      */   }
/*      */   
/*      */   public long getStartTime() {
/*  233 */     return this.mStartTime;
/*      */   }
/*      */   
/*      */   public long getEndTime() {
/*  237 */     return this.mEndTime;
/*      */   }
/*      */   
/*      */   public AlarmData getAlarmData() {
/*  241 */     return this.mAlarmData;
/*      */   }
/*      */   
/*      */   public void saveMetadata()
/*      */     throws ServiceException
/*      */   {
/*  247 */     reanalyze(null, getSize());
/*      */   }
/*      */   
/*      */ 
/*      */   boolean isTaggable()
/*      */   {
/*  253 */     return true;
/*      */   }
/*      */   
/*      */   boolean isCopyable()
/*      */   {
/*  258 */     return inDumpster();
/*      */   }
/*      */   
/*      */   boolean isMovable()
/*      */   {
/*  263 */     return true;
/*      */   }
/*      */   
/*      */   public boolean isMutable()
/*      */   {
/*  268 */     return true;
/*      */   }
/*      */   
/*      */   boolean canHaveChildren()
/*      */   {
/*  273 */     return false;
/*      */   }
/*      */   
/*      */   public List<IndexDocument> generateIndexData() throws MailItem.TemporaryIndexingException
/*      */   {
/*  278 */     List<IndexDocument> docs = null;
/*  279 */     this.mMailbox.lock.lock();
/*      */     try {
/*  281 */       docs = getIndexDocuments();
/*      */     } finally {
/*  283 */       this.mMailbox.lock.release();
/*      */     }
/*      */     
/*  286 */     return docs;
/*      */   }
/*      */   
/*      */   protected List<IndexDocument> getIndexDocuments() throws MailItem.TemporaryIndexingException {
/*  290 */     List<IndexDocument> toRet = new ArrayList();
/*      */     
/*      */ 
/*      */ 
/*  294 */     if (numInvites() < 1) {
/*  295 */       return toRet;
/*      */     }
/*  297 */     Invite defaultInvite = getDefaultInviteOrNull();
/*      */     
/*  299 */     String defaultLocation = "";
/*  300 */     if ((defaultInvite != null) && (defaultInvite.getLocation() != null)) {
/*  301 */       defaultLocation = defaultInvite.getLocation();
/*      */     }
/*  303 */     String defaultName = "";
/*  304 */     if ((defaultInvite != null) && (defaultInvite.getName() != null)) {
/*  305 */       defaultName = defaultInvite.getName();
/*      */     }
/*  307 */     String defaultOrganizer = "";
/*  308 */     if ((defaultInvite != null) && (defaultInvite.getOrganizer() != null))
/*  309 */       defaultOrganizer = defaultInvite.getOrganizer().getIndexString();
/*      */     StringBuilder s;
/*  311 */     List<String> toAddrs; String nameToUse; String orgToUse; for (Invite inv : getInvites()) {
/*  312 */       s = new StringBuilder();
/*  313 */       toAddrs = new ArrayList();
/*      */       
/*      */ 
/*  316 */       nameToUse = "";
/*  317 */       if (inv.getName() != null) {
/*  318 */         s.append(inv.getName()).append(' ');
/*  319 */         nameToUse = inv.getName();
/*      */       } else {
/*  321 */         s.append(defaultName).append(' ');
/*  322 */         nameToUse = defaultName;
/*      */       }
/*      */       
/*      */ 
/*  326 */       orgToUse = null;
/*  327 */       if (inv.getOrganizer() != null) {
/*  328 */         String thisInvOrg = inv.getOrganizer().getIndexString();
/*  329 */         if ((thisInvOrg != null) && (thisInvOrg.length() > 0))
/*  330 */           orgToUse = thisInvOrg;
/*      */       }
/*  332 */       if (orgToUse == null) {
/*  333 */         orgToUse = defaultOrganizer;
/*      */       }
/*      */       
/*  336 */       for (ZAttendee at : inv.getAttendees()) {
/*      */         try {
/*  338 */           toAddrs.add(at.getFriendlyAddress().toString());
/*  339 */           s.append(at.getIndexString()).append(' ');
/*      */         } catch (ServiceException e) {}
/*      */       }
/*  342 */       s.append(' ');
/*      */       
/*      */ 
/*  345 */       if (inv.getLocation() != null) {
/*  346 */         s.append(inv.getLocation()).append(' ');
/*      */       } else {
/*  348 */         s.append(defaultLocation).append(' ');
/*      */       }
/*      */       
/*      */       try
/*      */       {
/*  353 */         s.append(inv.getDescription()).append(' ');
/*      */       } catch (ServiceException ex) {
/*  355 */         if (ZimbraLog.index.isDebugEnabled()) {
/*  356 */           ZimbraLog.index.debug("Caught exception fetching description while indexing CalendarItem " + getId() + " skipping", ex);
/*      */         }
/*      */       }
/*      */       
/*      */ 
/*  361 */       List<String> comments = inv.getComments();
/*  362 */       if ((comments != null) && (!comments.isEmpty())) {
/*  363 */         for (String comm : comments) {
/*  364 */           s.append(comm).append(' ');
/*      */         }
/*      */       }
/*      */       
/*      */ 
/*  369 */       List<String> contacts = inv.getContacts();
/*  370 */       if ((contacts != null) && (!contacts.isEmpty())) {
/*  371 */         for (String contact : contacts) {
/*  372 */           s.append(contact).append(' ');
/*      */         }
/*      */       }
/*      */       
/*      */ 
/*  377 */       List<String> categories = inv.getCategories();
/*  378 */       if ((categories != null) && (!categories.isEmpty())) {
/*  379 */         for (String cat : categories) {
/*  380 */           s.append(cat).append(' ');
/*      */         }
/*      */       }
/*      */       
/*  384 */       MimeMessage mm = null;
/*      */       
/*  386 */       if (!inv.getDontIndexMimeMessage()) {
/*      */         try {
/*  388 */           mm = inv.getMimeMessage();
/*      */         } catch (ServiceException e) {
/*  390 */           if (ZimbraLog.index.isDebugEnabled()) {
/*  391 */             ZimbraLog.index.debug("Caught MessagingException for Invite " + inv.toString() + " while fetching MM during indexing of CalendarItem " + getId() + " skipping Invite", e);
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*  396 */       List<IndexDocument> docList = new ArrayList();
/*      */       
/*  398 */       if (mm == null) {
/*  399 */         IndexDocument doc = new IndexDocument();
/*      */         
/*  401 */         doc.addPartName("top");
/*  402 */         docList.add(doc);
/*      */       } else {
/*      */         try {
/*  405 */           ParsedMessage pm = new ParsedMessage(mm, this.mMailbox.attachmentsIndexingEnabled());
/*  406 */           pm.analyzeFully();
/*      */           
/*  408 */           if (pm.hasTemporaryAnalysisFailure()) {
/*  409 */             throw new MailItem.TemporaryIndexingException();
/*      */           }
/*  411 */           docList = pm.getLuceneDocuments();
/*      */         } catch (ServiceException e) {
/*  413 */           if (ZimbraLog.index.isDebugEnabled()) {
/*  414 */             ZimbraLog.index.debug("Caught MessagingException for Invite " + inv.toString() + " while indexing CalendarItem " + getId() + " skipping Invite", e);
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*  419 */       for (IndexDocument doc : docList)
/*      */       {
/*  421 */         doc.addContent(s.toString());
/*      */         
/*  423 */         doc.removeTo();
/*  424 */         doc.removeFrom();
/*  425 */         doc.removeSubject();
/*      */         
/*  427 */         for (String to : toAddrs) {
/*  428 */           doc.addTo(new RFC822AddressTokenStream(to));
/*      */         }
/*  430 */         doc.addFrom(new RFC822AddressTokenStream(orgToUse));
/*  431 */         doc.addSubject(nameToUse);
/*  432 */         toRet.add(doc);
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*  437 */     FieldTokenStream fields = new FieldTokenStream("_calendaritemclass", isPublic() ? "public" : "private");
/*  438 */     for (IndexDocument doc : toRet) {
/*  439 */       doc.addField(fields);
/*      */     }
/*      */     
/*  442 */     return toRet;
/*      */   }
/*      */   
/*      */   public String getSortSender()
/*      */   {
/*  447 */     String sender = new ParsedAddress(getSender()).getSortString();
/*      */     
/*  449 */     return DbMailItem.normalize(sender, 128);
/*      */   }
/*      */   
/*      */   static CalendarItem create(int id, Folder folder, int flags, Tag.NormalizedTags ntags, String uid, ParsedMessage pm, Invite firstInvite, long nextAlarm, MailItem.CustomMetadata custom) throws ServiceException
/*      */   {
/*  454 */     firstInvite.sanitize(false);
/*      */     
/*  456 */     if (!folder.canAccess((short)4)) {
/*  457 */       throw ServiceException.PERM_DENIED("you do not have the required rights on the folder");
/*      */     }
/*  459 */     if ((!firstInvite.isPublic()) && (!folder.canAccess((short)1024))) {
/*  460 */       throw ServiceException.PERM_DENIED("you do not have permission to create private calendar item in this folder");
/*      */     }
/*  462 */     Mailbox mbox = folder.getMailbox();
/*      */     
/*  464 */     if ((pm != null) && (pm.hasAttachments())) {
/*  465 */       firstInvite.setHasAttachment(true);
/*  466 */       flags |= Flag.BITMASK_ATTACHED;
/*      */     } else {
/*  468 */       firstInvite.setHasAttachment(false);
/*  469 */       flags &= (Flag.BITMASK_ATTACHED ^ 0xFFFFFFFF);
/*      */     }
/*  471 */     if (firstInvite.isDraft()) {
/*  472 */       flags |= Flag.BITMASK_DRAFT;
/*      */     } else {
/*  474 */       flags &= (Flag.BITMASK_DRAFT ^ 0xFFFFFFFF);
/*      */     }
/*  476 */     if (firstInvite.isHighPriority()) {
/*  477 */       flags |= Flag.BITMASK_HIGH_PRIORITY;
/*      */     } else {
/*  479 */       flags &= (Flag.BITMASK_HIGH_PRIORITY ^ 0xFFFFFFFF);
/*      */     }
/*  481 */     if (firstInvite.isLowPriority()) {
/*  482 */       flags |= Flag.BITMASK_LOW_PRIORITY;
/*      */     } else {
/*  484 */       flags &= (Flag.BITMASK_LOW_PRIORITY ^ 0xFFFFFFFF);
/*      */     }
/*  486 */     MailItem.Type type = firstInvite.isEvent() ? MailItem.Type.APPOINTMENT : MailItem.Type.TASK;
/*      */     
/*  488 */     String sender = null;
/*  489 */     ZOrganizer org = firstInvite.getOrganizer();
/*  490 */     if (org != null) {
/*  491 */       sender = org.getIndexString();
/*      */     }
/*  493 */     sender = Strings.nullToEmpty(sender);
/*  494 */     String subject = Strings.nullToEmpty(firstInvite.getName());
/*      */     
/*  496 */     List<Invite> invites = new ArrayList();
/*  497 */     invites.add(firstInvite);
/*      */     
/*  499 */     Recurrence.IRecurrence recur = firstInvite.getRecurrence();
/*      */     long endTime;
/*  501 */     long startTime; long endTime; if (recur != null) {
/*  502 */       ParsedDateTime dtStart = recur.getStartTime();
/*  503 */       long startTime = dtStart != null ? dtStart.getUtcTime() : 0L;
/*  504 */       ParsedDateTime dtEnd = recur.getEndTime();
/*  505 */       endTime = dtEnd != null ? dtEnd.getUtcTime() : 0L;
/*      */     } else {
/*  507 */       ParsedDateTime dtStart = firstInvite.getStartTime();
/*  508 */       startTime = dtStart != null ? dtStart.getUtcTime() : 0L;
/*  509 */       ParsedDateTime dtEnd = firstInvite.getEffectiveEndTime();
/*  510 */       endTime = dtEnd != null ? dtEnd.getUtcTime() : startTime;
/*      */     }
/*      */     
/*  513 */     Account account = mbox.getAccount();
/*  514 */     firstInvite.updateMyPartStat(account, firstInvite.getPartStat());
/*      */     
/*  516 */     MailItem.UnderlyingData data = new MailItem.UnderlyingData();
/*  517 */     data.id = id;
/*  518 */     data.type = type.toByte();
/*  519 */     data.folderId = folder.getId();
/*  520 */     if ((!folder.inSpam()) || (mbox.getAccount().getBooleanAttr("zimbraJunkMessagesIndexingEnabled", false))) {
/*  521 */       data.indexId = MailItem.IndexStatus.DEFERRED.id();
/*      */     }
/*  523 */     data.imapId = id;
/*  524 */     data.date = mbox.getOperationTimestamp();
/*  525 */     data.setFlags(flags & (Flag.FLAGS_CALITEM | Flag.FLAGS_GENERIC));
/*  526 */     data.setTags(ntags);
/*  527 */     data.setSubject(subject);
/*  528 */     data.metadata = encodeMetadata(DEFAULT_COLOR_RGB, 1, 1, custom, uid, startTime, endTime, recur, invites, firstInvite.getTimeZoneMap(), new ReplyList(), null);
/*      */     
/*  530 */     data.contentChanged(mbox, false);
/*      */     
/*  532 */     if (!firstInvite.hasRecurId()) {
/*  533 */       ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s", new Object[] { Integer.valueOf(data.id), pm != null ? pm.getMessageID() : "(none)", Integer.valueOf(folder.getId()), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid() });
/*      */ 
/*      */     }
/*      */     else
/*      */     {
/*      */ 
/*  539 */       ZimbraLog.calendar.info("Adding CalendarItem: id=%d, Message-ID=\"%s\", folderId=%d, subject=\"%s\", UID=%s, recurId=%s", new Object[] { Integer.valueOf(data.id), pm != null ? pm.getMessageID() : "(none)", Integer.valueOf(folder.getId()), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid(), firstInvite.getRecurId().getDtZ() });
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  546 */     new DbMailItem(mbox).setSender(sender).create(data);
/*      */     
/*  548 */     CalendarItem item = type == MailItem.Type.APPOINTMENT ? new Appointment(mbox, data) : new Task(mbox, data);
/*  549 */     Invite defInvite = item.getDefaultInviteOrNull();
/*  550 */     if (defInvite != null) {
/*  551 */       Collection<Instance> instances = item.expandInstances(-11644473600000L, Long.MAX_VALUE, false);
/*      */       
/*  553 */       if (instances.isEmpty()) {
/*  554 */         ZimbraLog.calendar.info("CalendarItem has effectively zero instances: id=%d, folderId=%d, subject=\"%s\", UID=%s ", new Object[] { Integer.valueOf(data.id), Integer.valueOf(folder.getId()), firstInvite.isPublic() ? firstInvite.getName() : "(private)", firstInvite.getUid() });
/*      */         
/*  556 */         item.delete();
/*  557 */         throw ServiceException.FORBIDDEN("Recurring series has effectively zero instances");
/*      */       }
/*      */     }
/*      */     
/*      */     String defaultPartStat;
/*      */     
/*      */     String defaultPartStat;
/*      */     
/*  565 */     if (mbox.getOperationContext() == null)
/*      */     {
/*  567 */       defaultPartStat = "NE";
/*      */     } else {
/*  569 */       defaultPartStat = firstInvite.getPartStat();
/*      */     }
/*  571 */     item.processPartStat(firstInvite, pm != null ? pm.getMimeMessage() : null, true, defaultPartStat);
/*  572 */     item.finishCreation(null);
/*  573 */     folder.updateHighestMODSEQ();
/*  574 */     if (pm != null) {
/*  575 */       item.createBlob(pm, firstInvite);
/*      */     }
/*  577 */     item.mEndTime = item.recomputeRecurrenceEndTime(item.mEndTime);
/*      */     
/*  579 */     if (firstInvite.hasAlarm()) {
/*  580 */       item.recomputeNextAlarm(nextAlarm, false, false);
/*  581 */       item.saveMetadata();
/*  582 */       AlarmData alarmData = item.getAlarmData();
/*  583 */       if (alarmData != null) {
/*  584 */         long newNextAlarm = alarmData.getNextAtBase();
/*  585 */         if ((newNextAlarm > 0L) && (newNextAlarm < item.mStartTime)) {
/*  586 */           item.mStartTime = newNextAlarm;
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*  591 */     DbMailItem.addToCalendarItemTable(item);
/*      */     
/*  593 */     Callback cb = getCallback();
/*  594 */     if (cb != null) {
/*  595 */       cb.created(item);
/*      */     }
/*  597 */     return item;
/*      */   }
/*      */   
/*      */   private long recomputeRecurrenceEndTime(long defaultVal) throws ServiceException
/*      */   {
/*  602 */     long endTime = defaultVal;
/*  603 */     if (this.mRecurrence != null) {
/*  604 */       ParsedDateTime e = this.mRecurrence.getEndTime();
/*  605 */       if (e != null)
/*  606 */         endTime = e.getUtcTime();
/*      */     }
/*  608 */     return endTime;
/*      */   }
/*      */   
/*      */   public int fixRecurrenceEndTime() throws ServiceException
/*      */   {
/*  613 */     long endTime = recomputeRecurrenceEndTime(this.mEndTime);
/*  614 */     if (endTime != this.mEndTime) {
/*  615 */       markItemModified(196608);
/*  616 */       this.mEndTime = endTime;
/*  617 */       DbMailItem.updateInCalendarItemTable(this);
/*  618 */       return 1;
/*      */     }
/*  620 */     return 0;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private Collection<Instance> instancesNear(RecurId recurId)
/*      */     throws ServiceException
/*      */   {
/*  631 */     if (recurId == null) {
/*  632 */       return Collections.emptyList();
/*      */     }
/*  634 */     ParsedDateTime dt = recurId.getDt();
/*  635 */     if (dt == null) {
/*  636 */       return Collections.emptyList();
/*      */     }
/*  638 */     long utcTime = dt.getUtcTime();
/*  639 */     return expandInstances(utcTime - 86400000L, utcTime + 86400000L, false);
/*      */   }
/*      */   
/*      */   private boolean instanceMatches(RecurId recurId, Collection<Instance> instances) {
/*  643 */     long utcTime = recurId.getDt().getUtcTime();
/*  644 */     for (Instance instance : instances) {
/*  645 */       if (utcTime == instance.getStart()) {
/*  646 */         return true;
/*      */       }
/*      */     }
/*  649 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */   private void checkExdateIsSensible(RecurId exdateRecurId)
/*      */     throws ServiceException
/*      */   {
/*  656 */     Collection<Instance> instancesNear = instancesNear(exdateRecurId);
/*  657 */     if (instancesNear.isEmpty()) {
/*  658 */       ZimbraLog.calendar.warn("WARNING:Adding EXDATE %s, however, that does not exclude any pre-existing instances.", new Object[] { exdateRecurId.toString() });
/*      */ 
/*      */ 
/*      */     }
/*  662 */     else if (!instanceMatches(exdateRecurId, instancesNear)) {
/*  663 */       ICalTimeZone exdateTZ = exdateRecurId.getDt().getTimeZone();
/*  664 */       StringBuilder sb = new StringBuilder();
/*  665 */       for (Instance instance : instancesNear) {
/*  666 */         long dtStart = instance.getStart();
/*  667 */         sb.append(" ").append(ParsedDateTime.fromUTCTime(dtStart, exdateTZ));
/*      */       }
/*  669 */       ZimbraLog.calendar.warn("WARNING:Adding EXDATE %s, however, that does not exclude any pre-existing instances.  Nearby times:%s", new Object[] { exdateRecurId.toString(), sb.toString() });
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private void checkRecurIdIsSensible(RecurId recurId)
/*      */     throws ServiceException
/*      */   {
/*  679 */     Collection<Instance> instancesNear = instancesNear(recurId);
/*  680 */     if (instancesNear.isEmpty()) {
/*  681 */       ZimbraLog.calendar.warn("WARNING:RECURRENCE-ID %s, does not match any pre-existing instances.", new Object[] { recurId.toString() });
/*      */ 
/*      */ 
/*      */     }
/*  685 */     else if (!instanceMatches(recurId, instancesNear)) {
/*  686 */       ICalTimeZone exdateTZ = recurId.getDt().getTimeZone();
/*  687 */       StringBuilder sb = new StringBuilder();
/*  688 */       for (Instance instance : instancesNear) {
/*  689 */         long dtStart = instance.getStart();
/*  690 */         sb.append(" ").append(ParsedDateTime.fromUTCTime(dtStart, exdateTZ));
/*      */       }
/*  692 */       ZimbraLog.calendar.warn("WARNING:RECURRENCE-ID %s, does not match any pre-existing instances.  Nearby times:%s", new Object[] { recurId.toString(), sb.toString() });
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean updateRecurrence(long nextAlarm)
/*      */     throws ServiceException
/*      */   {
/*  702 */     Invite firstInv = getDefaultInviteOrNull();
/*  703 */     if (firstInv == null) {
/*  704 */       return false;
/*      */     }
/*      */     
/*  707 */     Recurrence.IRecurrence recur = firstInv.getRecurrence();
/*  708 */     long endTime; long startTime; long endTime; if ((recur instanceof Recurrence.RecurrenceRule)) {
/*  709 */       this.mRecurrence = ((Recurrence.IRecurrence)recur.clone());
/*      */       
/*      */ 
/*  712 */       for (Invite cur : this.mInvites) {
/*  713 */         if (cur != firstInv) {
/*  714 */           String method = cur.getMethod();
/*  715 */           if (cur.isCancel()) {
/*  716 */             assert (cur.hasRecurId());
/*  717 */             if (cur.hasRecurId()) {
/*  718 */               checkExdateIsSensible(cur.getRecurId());
/*  719 */               Recurrence.CancellationRule cancelRule = new Recurrence.CancellationRule(cur.getRecurId());
/*      */               
/*  721 */               ((Recurrence.RecurrenceRule)this.mRecurrence).addException(cancelRule);
/*      */             }
/*  723 */           } else if ((method.equals(ZCalendar.ICalTok.REQUEST.toString())) || (method.equals(ZCalendar.ICalTok.PUBLISH.toString())))
/*      */           {
/*  725 */             assert (cur.hasRecurId());
/*  726 */             if ((cur.hasRecurId()) && (cur.getStartTime() != null)) {
/*  727 */               checkRecurIdIsSensible(cur.getRecurId());
/*  728 */               Recurrence.ExceptionRule exceptRule = null;
/*  729 */               Recurrence.IRecurrence curRule = cur.getRecurrence();
/*  730 */               if ((curRule != null) && ((curRule instanceof Recurrence.ExceptionRule))) {
/*  731 */                 exceptRule = (Recurrence.ExceptionRule)curRule.clone();
/*      */               }
/*      */               else {
/*  734 */                 exceptRule = new Recurrence.ExceptionRule(cur.getRecurId(), cur.getStartTime(), cur.getEffectiveDuration(), new InviteInfo(cur));
/*      */               }
/*      */               
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  741 */               ((Recurrence.RecurrenceRule)this.mRecurrence).addException(exceptRule);
/*      */             } else {
/*  743 */               sLog.debug("Got second invite with no RecurID: " + cur.toString());
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*      */ 
/*  750 */       ParsedDateTime earliestStart = null;
/*  751 */       ParsedDateTime latestEnd = null;
/*  752 */       for (Invite cur : this.mInvites) {
/*  753 */         if (!cur.isCancel()) {
/*  754 */           ParsedDateTime start = cur.getStartTime();
/*  755 */           if (earliestStart == null) {
/*  756 */             earliestStart = start;
/*  757 */           } else if ((start != null) && (start.compareTo(earliestStart) < 0))
/*  758 */             earliestStart = start;
/*  759 */           ParsedDateTime end = cur.getEffectiveEndTime();
/*  760 */           if (latestEnd == null) {
/*  761 */             latestEnd = end;
/*  762 */           } else if ((end != null) && (end.compareTo(latestEnd) > 0)) {
/*  763 */             latestEnd = end;
/*      */           }
/*      */         }
/*      */       }
/*  767 */       ParsedDateTime recurEnd = this.mRecurrence.getEndTime();
/*  768 */       if (latestEnd == null) {
/*  769 */         latestEnd = recurEnd;
/*  770 */       } else if ((recurEnd != null) && (recurEnd.compareTo(latestEnd) > 0)) {
/*  771 */         latestEnd = recurEnd;
/*      */       }
/*      */       
/*      */ 
/*  775 */       long startTime = earliestStart != null ? earliestStart.getUtcTime() : 0L;
/*  776 */       endTime = latestEnd != null ? latestEnd.getUtcTime() : 0L;
/*      */     } else {
/*  778 */       this.mRecurrence = null;
/*  779 */       startTime = 0L;endTime = 0L;
/*  780 */       for (Invite inv : this.mInvites) {
/*  781 */         if (!inv.isCancel()) {
/*  782 */           ParsedDateTime dtStart = inv.getStartTime();
/*  783 */           long st = dtStart != null ? dtStart.getUtcTime() : 0L;
/*  784 */           if ((st != 0L) && ((st < startTime) || (startTime == 0L)))
/*  785 */             startTime = st;
/*  786 */           ParsedDateTime dtEnd = inv.getEffectiveEndTime();
/*  787 */           long et = dtEnd != null ? dtEnd.getUtcTime() : 0L;
/*  788 */           if ((et != 0L) && (et > endTime)) {
/*  789 */             endTime = et;
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*  795 */     boolean timesChanged = false;
/*  796 */     if ((this.mStartTime != startTime) || (this.mEndTime != endTime)) {
/*  797 */       timesChanged = true;
/*  798 */       this.mStartTime = startTime;
/*  799 */       this.mEndTime = endTime;
/*      */     }
/*      */     
/*      */ 
/*  803 */     recomputeNextAlarm(nextAlarm, false, false);
/*  804 */     if (this.mAlarmData != null) {
/*  805 */       long newNextAlarm = this.mAlarmData.getNextAtBase();
/*  806 */       if ((newNextAlarm > 0L) && (newNextAlarm < startTime) && (this.mStartTime != startTime)) {
/*  807 */         timesChanged = true;
/*  808 */         this.mStartTime = newNextAlarm;
/*      */       }
/*      */     }
/*      */     
/*  812 */     if (timesChanged) {
/*  813 */       if (ZimbraLog.calendar.isDebugEnabled()) {
/*  814 */         ZimbraLog.calendar.debug("Updating recurrence for %s.  nextAlarm=%d.", new Object[] { getMailopContext(this), Long.valueOf(nextAlarm) });
/*      */       }
/*      */       
/*  817 */       DbMailItem.updateInCalendarItemTable(this);
/*      */     }
/*  819 */     return true;
/*      */   }
/*      */   
/*      */ 
/*      */   void decodeMetadata(Metadata meta)
/*      */     throws ServiceException
/*      */   {
/*  826 */     super.decodeMetadata(meta);
/*      */     
/*  828 */     this.mUid = Invite.fixupIfOutlookUid(meta.get("u", null));
/*  829 */     this.mInvites = new ArrayList();
/*      */     
/*  831 */     ICalTimeZone accountTZ = Util.getAccountTimeZone(getMailbox().getAccount());
/*  832 */     if (meta.containsKey("tzm")) {
/*      */       try {
/*  834 */         Set<String> tzids = new HashSet();
/*  835 */         this.mTzMap = Util.decodeFromMetadata(meta.getMap("tzm"), accountTZ);
/*      */         
/*      */ 
/*  838 */         this.mStartTime = meta.getLong("aps", 0L);
/*  839 */         this.mEndTime = meta.getLong("ape", 0L);
/*      */         
/*      */ 
/*  842 */         long numComp = meta.getLong("nc");
/*  843 */         for (int i = 0; i < numComp; i++) {
/*  844 */           Metadata md = meta.getMap("inv" + i);
/*  845 */           Invite inv = Invite.decodeMetadata(getMailboxId(), md, this, accountTZ);
/*  846 */           this.mInvites.add(inv);
/*  847 */           tzids.addAll(inv.getReferencedTZIDs());
/*  848 */           this.mTzMap.add(inv.getTimeZoneMap());
/*      */         }
/*      */         
/*  851 */         Metadata metaRecur = meta.getMap("apptRecur", true);
/*  852 */         if (metaRecur != null) {
/*  853 */           this.mRecurrence = Recurrence.decodeMetadata(metaRecur, this.mTzMap);
/*  854 */           if (this.mRecurrence != null) {
/*  855 */             tzids.addAll(Recurrence.getReferencedTZIDs(this.mRecurrence));
/*      */           }
/*      */         }
/*      */         
/*  859 */         if (meta.containsKey("rl")) {
/*  860 */           this.mReplyList = ReplyList.decodeFromMetadata(meta.getMap("rl"), this.mTzMap);
/*      */           
/*  862 */           for (ReplyInfo ri : this.mReplyList.mReplies) {
/*  863 */             if (ri.mRecurId != null) {
/*  864 */               ParsedDateTime dt = ri.mRecurId.getDt();
/*  865 */               if ((dt != null) && (dt.hasTime())) {
/*  866 */                 ICalTimeZone tz = dt.getTimeZone();
/*  867 */                 if (tz != null)
/*  868 */                   tzids.add(tz.getID());
/*      */               }
/*      */             }
/*      */           }
/*      */         } else {
/*  873 */           this.mReplyList = new ReplyList();
/*      */         }
/*      */         
/*  876 */         Metadata metaAlarmData = meta.getMap("ad", true);
/*  877 */         if (metaAlarmData != null) {
/*  878 */           this.mAlarmData = AlarmData.decodeMetadata(metaAlarmData);
/*      */         }
/*      */         
/*  881 */         this.mTzMap.reduceTo(tzids);
/*      */       } catch (ServiceException se) {
/*  883 */         if (("service.INVALID_REQUEST".equals(se.getCode())) && (getChangeDate() < new GregorianCalendar(2006, 0, 1).getTimeInMillis()))
/*      */         {
/*      */ 
/*      */ 
/*      */ 
/*  888 */           this.mStartTime = 0L;
/*  889 */           this.mEndTime = 0L;
/*      */         } else {
/*  891 */           throw se;
/*      */         }
/*      */       }
/*      */     }
/*      */     else {
/*  896 */       this.mStartTime = 0L;
/*  897 */       this.mEndTime = 0L;
/*      */     }
/*      */   }
/*      */   
/*      */   Metadata encodeMetadata(Metadata meta) {
/*  902 */     return encodeMetadata(meta, this.mRGBColor, this.mMetaVersion, this.mVersion, this.mExtendedData, this.mUid, this.mStartTime, this.mEndTime, this.mRecurrence, this.mInvites, this.mTzMap, this.mReplyList, this.mAlarmData);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private static String encodeMetadata(Color color, int metaVersion, int version, MailItem.CustomMetadata custom, String uid, long startTime, long endTime, Recurrence.IRecurrence recur, List<Invite> invs, TimeZoneMap tzmap, ReplyList replyList, AlarmData alarmData)
/*      */   {
/*  909 */     MailItem.CustomMetadata.CustomMetadataList extended = custom == null ? null : custom.asList();
/*  910 */     return encodeMetadata(new Metadata(), color, metaVersion, version, extended, uid, startTime, endTime, recur, invs, tzmap, replyList, alarmData).toString();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   static Metadata encodeMetadata(Metadata meta, Color color, int metaVersion, int version, MailItem.CustomMetadata.CustomMetadataList extended, String uid, long startTime, long endTime, Recurrence.IRecurrence recur, List<Invite> invs, TimeZoneMap tzmap, ReplyList replyList, AlarmData alarmData)
/*      */   {
/*  917 */     if (tzmap != null) {
/*  918 */       meta.put("tzm", Util.encodeAsMetadata(tzmap));
/*      */     }
/*  920 */     meta.put("u", uid);
/*  921 */     meta.put("aps", startTime);
/*  922 */     meta.put("ape", endTime);
/*  923 */     meta.put("nc", invs.size());
/*      */     
/*  925 */     if (replyList != null) {
/*  926 */       meta.put("rl", replyList.encodeAsMetadata());
/*      */     }
/*  928 */     int num = 0;
/*  929 */     for (Invite comp : invs) {
/*  930 */       meta.put("inv" + num++, Invite.encodeMetadata(comp));
/*      */     }
/*  932 */     if (recur != null) {
/*  933 */       meta.put("apptRecur", recur.encodeMetadata());
/*      */     }
/*  935 */     if (alarmData != null) {
/*  936 */       meta.put("ad", alarmData.encodeMetadata());
/*      */     }
/*  938 */     return MailItem.encodeMetadata(meta, color, null, metaVersion, version, extended);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Collection<Instance> expandInstances(long start, long end, boolean includeAlarmOnlyInstances)
/*      */     throws ServiceException
/*      */   {
/*  951 */     long endAdjusted = end;
/*  952 */     long alarmInstStart = 0L;
/*  953 */     if (includeAlarmOnlyInstances)
/*      */     {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*  959 */       if (this.mAlarmData != null) {
/*  960 */         alarmInstStart = this.mAlarmData.getNextInstanceStart();
/*  961 */         long nextAlarm = this.mAlarmData.getNextAtBase();
/*  962 */         if ((nextAlarm >= start) && (nextAlarm < end) && 
/*  963 */           (alarmInstStart >= end)) {
/*  964 */           endAdjusted = alarmInstStart + 1L;
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*  969 */     List<Instance> instances = new ArrayList();
/*  970 */     if (this.mRecurrence != null) {
/*  971 */       long startTime = System.currentTimeMillis();
/*  972 */       instances = Recurrence.expandInstances(this.mRecurrence, getId(), start, endAdjusted);
/*  973 */       if (ZimbraLog.calendar.isDebugEnabled()) {
/*  974 */         long elapsed = System.currentTimeMillis() - startTime;
/*  975 */         ZimbraLog.calendar.debug("RECURRENCE EXPANSION for appt/task %s: start=%s, end=%s; took %sms.  %s instances", new Object[] { Integer.valueOf(getId()), Long.valueOf(start), Long.valueOf(end), Long.valueOf(elapsed), Integer.valueOf(instances.size()) });
/*      */ 
/*      */ 
/*      */       }
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*      */     }
/*  984 */     else if (this.mInvites != null) {
/*  985 */       for (Invite inv : this.mInvites) {
/*  986 */         if (!inv.isCancel())
/*      */         {
/*  988 */           ParsedDateTime dtStart = inv.getStartTime();
/*  989 */           long invStart = dtStart != null ? dtStart.getUtcTime() : 0L;
/*  990 */           ParsedDateTime dtEnd = inv.getEffectiveEndTime();
/*  991 */           long invEnd = dtEnd != null ? dtEnd.getUtcTime() : 0L;
/*  992 */           if (((invStart < endAdjusted) && (invEnd > start)) || (dtStart == null)) {
/*  993 */             Instance inst = new Instance(getId(), new InviteInfo(inv), dtStart != null, dtEnd != null, invStart, invEnd, inv.isAllDayEvent(), dtStart != null ? dtStart.getOffset() : 0, dtEnd != null ? dtEnd.getOffset() : 0, inv.hasRecurId(), false);
/*      */             
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 1000 */             instances.add(inst);
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/* 1007 */     for (Iterator<Instance> iter = instances.iterator(); iter.hasNext();) {
/* 1008 */       Instance inst = (Instance)iter.next();
/* 1009 */       if ((inst.hasStart()) && (inst.hasEnd())) {
/* 1010 */         long instStart = inst.getStart();
/* 1011 */         long instEnd = inst.getEnd();
/*      */         
/*      */ 
/* 1014 */         if ((instStart != alarmInstStart) && ((instEnd <= start) || (instStart >= end))) {
/* 1015 */           iter.remove();
/*      */         }
/*      */       }
/*      */     }
/* 1019 */     return instances;
/*      */   }
/*      */   
/*      */ 
/*      */   public static class Instance
/*      */     implements Comparable<Instance>
/*      */   {
/*      */     private final boolean mHasStart;
/*      */     
/*      */     private final boolean mHasEnd;
/*      */     
/*      */     private long mStart;
/*      */     
/*      */     private long mEnd;
/*      */     
/*      */     private boolean mIsException;
/*      */     
/*      */     private final boolean mFromRdate;
/*      */     
/*      */     private final InviteInfo mInvId;
/*      */     
/*      */     private final int mCalItemId;
/*      */     private final boolean mAllDay;
/*      */     private int mStartTzOffset;
/*      */     private int mEndTzOffset;
/*      */     
/*      */     public static Instance fromInvite(int calItemId, Invite inv)
/*      */     {
/* 1047 */       ParsedDateTime dtStart = inv.getStartTime();
/* 1048 */       long start = dtStart != null ? dtStart.getUtcTime() : 0L;
/* 1049 */       ParsedDateTime dtEnd = inv.getEffectiveEndTime();
/* 1050 */       long end = dtEnd != null ? dtEnd.getUtcTime() : 0L;
/* 1051 */       int startTzo = 0;int endTzo = 0;
/* 1052 */       boolean allDay = inv.isAllDayEvent();
/* 1053 */       if (allDay) {
/* 1054 */         if (dtStart != null)
/* 1055 */           startTzo = dtStart.getOffset();
/* 1056 */         if (dtEnd != null)
/* 1057 */           endTzo = dtEnd.getOffset();
/*      */       }
/* 1059 */       return new Instance(calItemId, new InviteInfo(inv), dtStart != null, dtEnd != null, start, end, allDay, startTzo, endTzo, inv.hasRecurId(), false);
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     public Instance(int calItemId, InviteInfo invInfo, boolean hasStart, boolean hasEnd, long start, long end, boolean allDay, int startTzOffset, int endTzOffset, boolean _exception, boolean fromRdate)
/*      */     {
/* 1068 */       this.mInvId = invInfo;
/* 1069 */       this.mCalItemId = calItemId;
/* 1070 */       this.mHasStart = hasStart;
/* 1071 */       this.mHasEnd = hasEnd;
/* 1072 */       this.mAllDay = allDay;
/* 1073 */       if (this.mHasStart) {
/* 1074 */         this.mStart = start;
/* 1075 */         this.mStartTzOffset = (this.mAllDay ? startTzOffset : 0);
/*      */       } else {
/* 1077 */         this.mStart = 0L;
/* 1078 */         this.mStartTzOffset = 0;
/*      */       }
/* 1080 */       if (this.mHasEnd) {
/* 1081 */         this.mEnd = end;
/* 1082 */         this.mEndTzOffset = (this.mAllDay ? endTzOffset : 0);
/*      */       } else {
/* 1084 */         this.mEnd = 0L;
/* 1085 */         this.mEndTzOffset = 0;
/*      */       }
/* 1087 */       this.mIsException = _exception;
/* 1088 */       this.mFromRdate = fromRdate;
/*      */     }
/*      */     
/*      */     public int compareTo(Instance other)
/*      */     {
/* 1093 */       long toRet = this.mCalItemId - other.mCalItemId;
/* 1094 */       if (toRet == 0L) {
/* 1095 */         toRet = compareTimes(this.mHasStart, this.mStart, this.mAllDay, this.mStartTzOffset, other.mHasStart, other.mStart, other.mAllDay, other.mStartTzOffset);
/*      */         
/* 1097 */         if (toRet == 0L) {
/* 1098 */           toRet = compareTimes(this.mHasEnd, this.mEnd, this.mAllDay, this.mEndTzOffset, other.mHasEnd, other.mEnd, other.mAllDay, other.mEndTzOffset);
/*      */           
/* 1100 */           if (toRet == 0L) {
/* 1101 */             if (this.mInvId != null) {
/* 1102 */               toRet = this.mInvId.compareTo(other.mInvId);
/* 1103 */             } else if (other.mInvId != null)
/* 1104 */               toRet = other.mInvId.compareTo(this.mInvId) * -1;
/*      */           }
/*      */         }
/*      */       }
/* 1108 */       if (toRet > 0L)
/* 1109 */         return 1;
/* 1110 */       if (toRet < 0L) {
/* 1111 */         return -1;
/*      */       }
/* 1113 */       return 0;
/*      */     }
/*      */     
/*      */     public boolean equals(Object o)
/*      */     {
/* 1118 */       if (!(o instanceof Instance)) {
/* 1119 */         return false;
/*      */       }
/*      */       
/* 1122 */       Instance other = (Instance)o;
/*      */       boolean sameInvId;
/* 1124 */       boolean sameInvId; if (this.mInvId != null) {
/* 1125 */         sameInvId = this.mInvId.equals(other.mInvId);
/*      */       } else
/* 1127 */         sameInvId = other.mInvId == null;
/* 1128 */       return (sameInvId) && (sameTime(other));
/*      */     }
/*      */     
/*      */     public boolean sameTime(Instance other)
/*      */     {
/* 1133 */       if (this.mAllDay != other.mAllDay) {
/* 1134 */         return false;
/*      */       }
/*      */       
/* 1137 */       if (this.mHasStart != other.mHasStart)
/* 1138 */         return false;
/* 1139 */       if (this.mHasStart)
/*      */       {
/* 1141 */         if ((this.mStart != other.mStart) || (this.mStartTzOffset != other.mStartTzOffset)) {
/* 1142 */           return false;
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 1147 */       if (this.mHasEnd != other.mHasEnd)
/* 1148 */         return false;
/* 1149 */       if (this.mHasEnd)
/*      */       {
/* 1151 */         if ((this.mEnd != other.mEnd) || (this.mEndTzOffset != other.mEndTzOffset)) {
/* 1152 */           return false;
/*      */         }
/*      */       }
/*      */       
/* 1156 */       return true;
/*      */     }
/*      */     
/*      */     public String toString()
/*      */     {
/* 1161 */       StringBuilder toRet = new StringBuilder("INST(");
/* 1162 */       Date dstart = new Date(this.mStart);
/* 1163 */       Date dend = new Date(this.mEnd);
/* 1164 */       toRet.append(this.mHasStart).append(",").append(this.mHasEnd).append(",");
/* 1165 */       toRet.append(dstart).append(",").append(dend).append(",").append(this.mIsException);
/* 1166 */       toRet.append(",allDay=").append(this.mAllDay);
/* 1167 */       toRet.append(",startTzo=").append(this.mStartTzOffset).append(",endTzo=").append(this.mEndTzOffset);
/* 1168 */       if (this.mInvId != null)
/* 1169 */         toRet.append(",ID=").append(this.mInvId.getMsgId()).append("-").append(this.mInvId.getComponentId());
/* 1170 */       toRet.append(")");
/* 1171 */       return toRet.toString();
/*      */     }
/*      */     
/* 1174 */     public int getCalendarItemId() { return this.mCalItemId; }
/* 1175 */     public CalendarItem getCalendarItem() { return null; }
/* 1176 */     public int getMailItemId() { return this.mInvId != null ? this.mInvId.getMsgId() : -1; }
/* 1177 */     public int getComponentNum() { return this.mInvId != null ? this.mInvId.getComponentId() : -1; }
/* 1178 */     public long getStart() { return this.mStart; }
/* 1179 */     public long getEnd() { return this.mEnd; }
/* 1180 */     public boolean isAllDay() { return this.mAllDay; }
/* 1181 */     public int getStartTzOffset() { return this.mStartTzOffset; }
/* 1182 */     public int getEndTzOffset() { return this.mEndTzOffset; }
/* 1183 */     public boolean hasStart() { return this.mHasStart; }
/* 1184 */     public boolean hasEnd() { return this.mHasEnd; }
/* 1185 */     public boolean isException() { return this.mIsException; }
/* 1186 */     public void setIsException(boolean isException) { this.mIsException = isException; }
/* 1187 */     public boolean fromRdate() { return this.mFromRdate; }
/* 1188 */     public InviteInfo getInviteInfo() { return this.mInvId; }
/*      */     
/*      */     public static class StartTimeComparator implements Comparator<CalendarItem.Instance>
/*      */     {
/*      */       public int compare(CalendarItem.Instance a, CalendarItem.Instance b) {
/* 1193 */         return CalendarItem.Instance.compareTimes(a.mHasStart, a.mStart, a.mAllDay, a.mStartTzOffset, b.mHasStart, b.mStart, b.mAllDay, b.mStartTzOffset);
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */     private static int compareTimes(boolean hasTimeA, long timeA, boolean allDayA, int tzoA, boolean hasTimeB, long timeB, boolean allDayB, int tzoB)
/*      */     {
/* 1201 */       if (!hasTimeA) {
/* 1202 */         if (hasTimeB) {
/* 1203 */           return -1;
/*      */         }
/* 1205 */         return 0;
/*      */       }
/* 1207 */       if (!hasTimeB) {
/* 1208 */         return 1;
/*      */       }
/*      */       
/*      */ 
/* 1212 */       if (timeA < timeB)
/* 1213 */         return -1;
/* 1214 */       if (timeA > timeB) {
/* 1215 */         return 1;
/*      */       }
/* 1217 */       if (allDayA == allDayB) {
/* 1218 */         if (tzoA > tzoB)
/* 1219 */           return -1;
/* 1220 */         if (tzoA < tzoB) {
/* 1221 */           return 1;
/*      */         }
/* 1223 */         return 0;
/*      */       }
/* 1225 */       if (allDayA) {
/* 1226 */         return -1;
/*      */       }
/* 1228 */       return 1;
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     public String getRecurIdZ()
/*      */     {
/* 1238 */       if (!this.mHasStart)
/* 1239 */         return null;
/*      */       ParsedDateTime dt;
/* 1241 */       if (this.mAllDay) {
/* 1242 */         ParsedDateTime dt = ParsedDateTime.fromUTCTime(this.mStart + this.mStartTzOffset);
/* 1243 */         dt.setHasTime(false);
/*      */       } else {
/* 1245 */         dt = ParsedDateTime.fromUTCTime(this.mStart);
/*      */       }
/* 1247 */       return dt.getDateTimePartString(false);
/*      */     }
/*      */     
/*      */     public RecurId makeRecurId(Invite refInv)
/*      */     {
/* 1252 */       RecurId rid = this.mInvId != null ? this.mInvId.getRecurrenceId() : null;
/* 1253 */       if (rid != null) {
/* 1254 */         return rid;
/*      */       }
/*      */       
/*      */ 
/* 1258 */       if (refInv == null) {
/* 1259 */         if (this.mHasStart) {
/* 1260 */           return new RecurId(ParsedDateTime.fromUTCTime(this.mStart), RecurId.RANGE_NONE);
/*      */         }
/*      */         
/* 1263 */         return null;
/*      */       }
/*      */       
/* 1266 */       ParsedDateTime dtStart = refInv.getStartTime();
/* 1267 */       if (dtStart == null)
/* 1268 */         return null;
/* 1269 */       ICalTimeZone tz = dtStart.getTimeZone();
/* 1270 */       long startTime = this.mStart;
/*      */       ParsedDateTime dt;
/* 1272 */       ParsedDateTime dt; if (tz != null) {
/* 1273 */         dt = ParsedDateTime.fromUTCTime(startTime, tz);
/*      */       } else
/* 1275 */         dt = ParsedDateTime.fromUTCTime(startTime);
/* 1276 */       if (refInv.isAllDayEvent())
/* 1277 */         dt.setHasTime(false);
/* 1278 */       return new RecurId(dt, RecurId.RANGE_NONE);
/*      */     }
/*      */   }
/*      */   
/*      */   public String getUid() {
/* 1283 */     return this.mUid;
/*      */   }
/*      */   
/*      */   public Invite getInvite(int invId, int compNum) {
/* 1287 */     for (Invite inv : this.mInvites) {
/* 1288 */       if ((inv.getMailItemId() == invId) && (inv.getComponentNum() == compNum)) {
/* 1289 */         return inv;
/*      */       }
/*      */     }
/* 1292 */     return null;
/*      */   }
/*      */   
/*      */   public Invite[] getInvites() {
/* 1296 */     int num = this.mInvites.size();
/* 1297 */     if (num == 1) {
/* 1298 */       Invite[] ret = new Invite[1];
/* 1299 */       ret[0] = ((Invite)this.mInvites.get(0));
/* 1300 */       return ret;
/*      */     }
/* 1302 */     ArrayList<Invite> toRet = new ArrayList(this.mInvites.size());
/*      */     
/* 1304 */     for (Invite inv : this.mInvites)
/* 1305 */       if (!inv.hasRecurId())
/* 1306 */         toRet.add(inv);
/* 1307 */     for (Invite inv : this.mInvites)
/* 1308 */       if (inv.hasRecurId())
/* 1309 */         toRet.add(inv);
/* 1310 */     return (Invite[])toRet.toArray(new Invite[0]);
/*      */   }
/*      */   
/*      */   public Invite[] getInvites(int invId)
/*      */   {
/* 1315 */     ArrayList<Invite> toRet = new ArrayList();
/* 1316 */     for (Invite inv : this.mInvites)
/* 1317 */       if (inv.getMailItemId() == invId)
/* 1318 */         toRet.add(inv);
/* 1319 */     return (Invite[])toRet.toArray(new Invite[0]);
/*      */   }
/*      */   
/*      */   public int numInvites() {
/* 1323 */     return this.mInvites.size();
/*      */   }
/*      */   
/*      */   public Invite getInvite(int index) {
/* 1327 */     return (Invite)this.mInvites.get(index);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Invite getInvite(RecurId rid)
/*      */   {
/* 1336 */     if (rid == null) {
/* 1337 */       for (Invite inv : this.mInvites) {
/* 1338 */         if (inv.getRecurId() == null)
/* 1339 */           return inv;
/*      */       }
/*      */     } else {
/* 1342 */       for (Invite inv : this.mInvites) {
/* 1343 */         if (rid.equals(inv.getRecurId()))
/* 1344 */           return inv;
/*      */       }
/*      */     }
/* 1347 */     return null;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Invite getInviteForRecurId(long recurIdDtstamp)
/*      */   {
/* 1358 */     Invite defInv = null;
/* 1359 */     for (Invite inv : this.mInvites) {
/* 1360 */       RecurId rid = inv.getRecurId();
/* 1361 */       if (rid == null) {
/* 1362 */         if (defInv == null)
/* 1363 */           defInv = inv;
/*      */       } else {
/* 1365 */         ParsedDateTime dt = rid.getDt();
/* 1366 */         if ((dt != null) && 
/* 1367 */           (dt.getUtcTime() == recurIdDtstamp)) {
/* 1368 */           return inv;
/*      */         }
/*      */       }
/*      */     }
/* 1372 */     return defInv;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Invite getInviteForRecurIdZ(String recurIdZ)
/*      */   {
/* 1384 */     Invite defInv = null;
/* 1385 */     for (Invite inv : this.mInvites) {
/* 1386 */       RecurId rid = inv.getRecurId();
/* 1387 */       if (recurIdZ != null) {
/* 1388 */         if (rid == null) {
/* 1389 */           if (defInv == null) {
/* 1390 */             defInv = inv;
/*      */           }
/* 1392 */         } else if (recurIdZ.equals(rid.getDtZ())) {
/* 1393 */           return inv;
/*      */         }
/*      */       }
/* 1396 */       else if (rid == null) {
/* 1397 */         return inv;
/*      */       }
/*      */     }
/* 1400 */     return defInv;
/*      */   }
/*      */   
/*      */   public Invite getInviteByMailItem(int mailItemId) {
/* 1404 */     for (Invite cur : this.mInvites) {
/* 1405 */       if (cur.getMailItemId() == mailItemId) {
/* 1406 */         return cur;
/*      */       }
/*      */     }
/* 1409 */     return null;
/*      */   }
/*      */   
/*      */   public Invite getDefaultInviteOrNull() {
/* 1413 */     Invite first = null;
/* 1414 */     for (Invite cur : this.mInvites) {
/* 1415 */       if (!cur.hasRecurId())
/* 1416 */         return cur;
/* 1417 */       if (first == null)
/* 1418 */         first = cur;
/*      */     }
/* 1420 */     if (first == null) {
/* 1421 */       ZimbraLog.calendar.error("Invalid state: appointment/task " + getId() + " in mailbox " + getMailbox().getId() + " has no default invite; " + (this.mInvites != null ? "invite count = " + this.mInvites.size() : "null invite list"));
/*      */     }
/*      */     
/* 1424 */     return first;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   public boolean isPublic()
/*      */   {
/* 1432 */     boolean result = true;
/* 1433 */     Invite[] invs = getInvites();
/* 1434 */     if ((invs != null) && (invs.length > 0)) {
/* 1435 */       for (Invite i : invs) {
/* 1436 */         if (!i.isPublic()) {
/* 1437 */           result = false;
/* 1438 */           break;
/*      */         }
/*      */       }
/*      */     }
/* 1442 */     return result;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public boolean allowPrivateAccess(Account authAccount, boolean asAdmin)
/*      */     throws ServiceException
/*      */   {
/* 1455 */     return canAccess((short)1024, authAccount, asAdmin);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static boolean allowPrivateAccess(Folder folder, Account authAccount, boolean asAdmin)
/*      */     throws ServiceException
/*      */   {
/* 1468 */     return folder.canAccess((short)1024, authAccount, asAdmin);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static boolean allowFreeBusyAccess(Folder folder, Account authAccount, boolean asAdmin)
/*      */     throws ServiceException
/*      */   {
/* 1481 */     return folder.canAccess((short)2048, authAccount, asAdmin);
/*      */   }
/*      */   
/*      */   boolean processNewInvite(ParsedMessage pm, Invite invite, int folderId, boolean replaceExistingInvites)
/*      */     throws ServiceException
/*      */   {
/* 1487 */     return processNewInvite(pm, invite, folderId, 0L, true, replaceExistingInvites);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   boolean processNewInvite(ParsedMessage pm, Invite invite, int folderId, long nextAlarm, boolean preserveAlarms, boolean replaceExistingInvites)
/*      */     throws ServiceException
/*      */   {
/* 1504 */     invite.setHasAttachment(pm != null ? pm.hasAttachments() : false);
/*      */     
/* 1506 */     String method = invite.getMethod();
/* 1507 */     if ((method.equals(ZCalendar.ICalTok.REQUEST.toString())) || (method.equals(ZCalendar.ICalTok.CANCEL.toString())) || (method.equals(ZCalendar.ICalTok.PUBLISH.toString())))
/*      */     {
/*      */ 
/* 1510 */       return processNewInviteRequestOrCancel(pm, invite, folderId, nextAlarm, preserveAlarms, replaceExistingInvites, false);
/*      */     }
/* 1512 */     if (method.equals(ZCalendar.ICalTok.REPLY.toString()))
/* 1513 */       return processNewInviteReply(invite, null);
/* 1514 */     if (method.equals(ZCalendar.ICalTok.COUNTER.toString())) {
/* 1515 */       return processNewInviteReply(invite, pm.getSender());
/*      */     }
/*      */     
/* 1518 */     if (!method.equals(ZCalendar.ICalTok.DECLINECOUNTER.toString()))
/* 1519 */       ZimbraLog.calendar.warn("Unsupported METHOD " + method);
/* 1520 */     return false;
/*      */   }
/*      */   
/* 1523 */   private boolean persistBatchedChanges = false;
/*      */   
/*      */   void processNewInviteExceptions(List<Mailbox.SetCalendarItemData> scidList, int folderId, long nextAlarm, boolean preserveAlarms, boolean replaceExistingInvites) throws ServiceException
/*      */   {
/* 1527 */     this.persistBatchedChanges = false;
/* 1528 */     for (Mailbox.SetCalendarItemData scid : scidList) {
/* 1529 */       processNewInviteRequestOrCancel(scid.message, scid.invite, folderId, nextAlarm, preserveAlarms, replaceExistingInvites, true);
/*      */     }
/*      */     
/*      */ 
/* 1533 */     updateRecurrence(nextAlarm);
/* 1534 */     if (this.persistBatchedChanges) {
/*      */       try
/*      */       {
/* 1537 */         setContent(null, null);
/*      */       } catch (IOException e) {
/* 1539 */         throw ServiceException.FAILURE("IOException", e);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private static boolean recurrenceIdsMatch(Invite inv1, Invite inv2)
/*      */   {
/* 1551 */     RecurId r1 = inv1.getRecurId();
/* 1552 */     RecurId r2 = inv2.getRecurId();
/* 1553 */     if (r1 != null) {
/* 1554 */       return r1.equals(r2);
/*      */     }
/* 1556 */     return r2 == null;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean processNewInviteRequestOrCancel(ParsedMessage pm, Invite newInvite, int folderId, long nextAlarm, boolean preserveAlarms, boolean discardExistingInvites, boolean batch)
/*      */     throws ServiceException
/*      */   {
/* 1582 */     if (!newInvite.hasRecurId()) {
/* 1583 */       ZimbraLog.calendar.info("Modifying CalendarItem: id=%d, folderId=%d, method=%s, subject=\"%s\", UID=%s", new Object[] { Integer.valueOf(this.mId), Integer.valueOf(getFolderId()), newInvite.getMethod(), newInvite.isPublic() ? newInvite.getName() : "(private)", this.mUid });
/*      */ 
/*      */     }
/*      */     else
/*      */     {
/* 1588 */       ZimbraLog.calendar.info("Modifying CalendarItem: id=%d, folderId=%d, method=%s, subject=\"%s\", UID=%s, recurId=%s", new Object[] { Integer.valueOf(this.mId), Integer.valueOf(getFolderId()), newInvite.getMethod(), newInvite.isPublic() ? newInvite.getName() : "(private)", this.mUid, newInvite.getRecurId().getDtZ() });
/*      */     }
/*      */     
/*      */ 
/*      */ 
/* 1593 */     newInvite.sanitize(false);
/*      */     
/* 1595 */     OperationContext octxt = getMailbox().getOperationContext();
/* 1596 */     Account authAccount = octxt != null ? octxt.getAuthenticatedUser() : null;
/* 1597 */     boolean asAdmin = octxt != null ? octxt.isUsingAdminPrivileges() : false;
/* 1598 */     boolean isCancel = newInvite.isCancel();
/*      */     
/* 1600 */     boolean requirePrivateCheck = requirePrivateCheck(newInvite);
/* 1601 */     short rightsNeeded = isCancel ? 10 : 2;
/* 1602 */     if (!canAccess(rightsNeeded, authAccount, asAdmin, requirePrivateCheck)) {
/* 1603 */       throw ServiceException.PERM_DENIED("you do not have sufficient permissions on this calendar item");
/*      */     }
/*      */     
/*      */ 
/* 1607 */     boolean isCalendarResource = getMailbox().getAccount() instanceof CalendarResource;
/* 1608 */     boolean denyPrivateAccess = !allowPrivateAccess(authAccount, asAdmin);
/* 1609 */     if (((!newInvite.isPublic()) || (!isPublic())) && 
/* 1610 */       (folderId != getFolderId())) {
/* 1611 */       Folder folder = getMailbox().getFolderById(folderId);
/* 1612 */       if (!allowPrivateAccess(folder, authAccount, asAdmin)) {
/* 1613 */         denyPrivateAccess = true;
/* 1614 */         if (!isCalendarResource) {
/* 1615 */           throw ServiceException.PERM_DENIED("you do not have permission to update/cancel private calendar item in target folder");
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/* 1621 */     boolean organizerChanged = organizerChangeCheck(newInvite, true);
/* 1622 */     ZOrganizer newOrganizer = newInvite.getOrganizer();
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 1628 */     if (isCancel) { boolean outdated;
/*      */       boolean cancelAll;
/*      */       boolean outdated;
/* 1631 */       if (!newInvite.hasRecurId()) {
/* 1632 */         boolean cancelAll = true;
/*      */         
/* 1634 */         Invite series = getInvite((RecurId)null);
/*      */         
/* 1636 */         outdated = (series != null) && (!newInvite.isSameOrNewerVersion(series));
/*      */       }
/*      */       else
/*      */       {
/* 1640 */         cancelAll = false;
/* 1641 */         outdated = false;
/* 1642 */         Invite curr = getInvite(newInvite.getRecurId());
/* 1643 */         if (curr != null) {
/* 1644 */           if (newInvite.isSameOrNewerVersion(curr)) {
/* 1645 */             cancelAll = true;
/*      */             
/* 1647 */             for (Invite inv : this.mInvites) {
/* 1648 */               if ((!inv.equals(curr)) && (!inv.isCancel())) {
/* 1649 */                 cancelAll = false;
/* 1650 */                 break;
/*      */               }
/*      */             }
/*      */           }
/*      */           else {
/* 1655 */             outdated = true;
/*      */           }
/*      */         }
/*      */       }
/* 1659 */       if (outdated) {
/* 1660 */         ZimbraLog.calendar.info("Ignoring outdated cancel request");
/* 1661 */         return false;
/*      */       }
/* 1663 */       if (cancelAll) {
/* 1664 */         Folder trash = this.mMailbox.getFolderById(3);
/* 1665 */         move(trash);
/*      */         
/* 1667 */         if (getMaxRevisions() != 1)
/* 1668 */           saveMetadata();
/* 1669 */         return true;
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/* 1675 */     Folder folder = getMailbox().getFolderById(folderId);
/* 1676 */     if ((!isCancel) && (discardExistingInvites) && (inTrash()) && (!folder.inTrash())) {
/* 1677 */       this.mReplyList.mReplies.clear();
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/* 1683 */     boolean needRecurrenceIdUpdate = false;
/* 1684 */     ParsedDateTime oldDtStart = null;
/* 1685 */     ParsedDuration dtStartMovedBy = null;
/* 1686 */     ArrayList<Invite> toUpdate = new ArrayList();
/* 1687 */     if ((!discardExistingInvites) && (!isCancel) && (newInvite.isRecurrence())) {
/* 1688 */       Invite defInv = getDefaultInviteOrNull();
/* 1689 */       if ((defInv != null) && (defInv.isRecurrence())) {
/* 1690 */         if (!getAccount().isCalendarKeepExceptionsOnSeriesTimeChange()) {
/* 1691 */           InviteChanges ic = new InviteChanges(defInv, newInvite);
/* 1692 */           if (ic.isExceptionRemovingChange()) {
/* 1693 */             discardExistingInvites = true;
/*      */           }
/*      */           
/*      */         }
/*      */         else
/*      */         {
/* 1699 */           oldDtStart = defInv.getStartTime();
/* 1700 */           ParsedDateTime newDtStart = newInvite.getStartTime();
/*      */           
/* 1702 */           if ((newDtStart != null) && (oldDtStart != null) && (!newDtStart.equals(oldDtStart)))
/*      */           {
/* 1704 */             ZRecur.Frequency freq = null;
/* 1705 */             Recurrence.IRecurrence recurrence = newInvite.getRecurrence();
/* 1706 */             if (recurrence != null) {
/* 1707 */               Iterator rulesIter = recurrence.addRulesIterator();
/* 1708 */               if (rulesIter != null) {
/* 1709 */                 while (rulesIter.hasNext()) {
/* 1710 */                   Object ruleObj = rulesIter.next();
/* 1711 */                   if ((ruleObj instanceof Recurrence.SimpleRepeatingRule)) {
/* 1712 */                     Recurrence.SimpleRepeatingRule series = (Recurrence.SimpleRepeatingRule)ruleObj;
/* 1713 */                     ZRecur recur = series.getRule();
/* 1714 */                     freq = recur.getFrequency();
/* 1715 */                     break;
/*      */                   }
/*      */                 }
/*      */               }
/*      */             }
/*      */             
/* 1721 */             ParsedDuration deltaLimit = null;
/* 1722 */             if (freq != null) {
/* 1723 */               switch (freq) {
/*      */               case DAILY: 
/* 1725 */                 deltaLimit = ParsedDuration.ONE_DAY;
/* 1726 */                 break;
/*      */               
/*      */ 
/*      */ 
/*      */               case WEEKLY: 
/*      */               case MONTHLY: 
/*      */               case YEARLY: 
/* 1733 */                 deltaLimit = ParsedDuration.ONE_WEEK;
/* 1734 */                 break;
/*      */               }
/*      */               
/*      */             }
/*      */             
/*      */ 
/* 1740 */             if (deltaLimit != null) {
/* 1741 */               ParsedDuration delta = newDtStart.difference(oldDtStart);
/* 1742 */               if (delta.abs().compareTo(deltaLimit) < 0) {
/* 1743 */                 needRecurrenceIdUpdate = true;
/* 1744 */                 dtStartMovedBy = delta;
/*      */               }
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */     Iterator<Alarm> alarmIter;
/*      */     
/* 1754 */     if ((!discardExistingInvites) && (preserveAlarms)) {
/* 1755 */       Invite localSeries = null;
/* 1756 */       Invite alarmSourceInv = null;
/* 1757 */       for (Invite inv : this.mInvites) {
/* 1758 */         if (recurrenceIdsMatch(inv, newInvite)) {
/* 1759 */           alarmSourceInv = inv;
/* 1760 */           break;
/*      */         }
/* 1762 */         if (!inv.hasRecurId())
/* 1763 */           localSeries = inv;
/*      */       }
/* 1765 */       if (alarmSourceInv == null)
/* 1766 */         alarmSourceInv = localSeries;
/* 1767 */       if (alarmSourceInv != null) {
/* 1768 */         newInvite.clearAlarms();
/* 1769 */         for (alarmIter = alarmSourceInv.alarmsIterator(); alarmIter.hasNext();) {
/* 1770 */           newInvite.addAlarm((Alarm)alarmIter.next());
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/* 1777 */     boolean zcoSeriesUpdate = false;
/* 1778 */     ZCalendar.ZProperty xzDiscardExcepts = newInvite.getXProperty(ZCalendar.ICalTok.X_ZIMBRA_DISCARD_EXCEPTIONS.toString());
/* 1779 */     if (xzDiscardExcepts != null) {
/* 1780 */       zcoSeriesUpdate = xzDiscardExcepts.getBoolValue();
/*      */     }
/*      */     
/*      */ 
/* 1784 */     long seriesUntil = Long.MAX_VALUE;
/* 1785 */     if ((!isCancel) && (!newInvite.hasRecurId())) {
/* 1786 */       ParsedDateTime dtStart = newInvite.getStartTime();
/* 1787 */       Recurrence.IRecurrence recur = newInvite.getRecurrence();
/* 1788 */       if ((recur != null) && (dtStart != null)) {
/* 1789 */         ICalTimeZone tz = dtStart.getTimeZone();
/*      */         
/* 1791 */         Iterator<?> iter = recur.addRulesIterator();
/* 1792 */         if (iter != null) {
/* 1793 */           while (iter.hasNext()) {
/* 1794 */             Recurrence.IRecurrence cur = (Recurrence.IRecurrence)iter.next();
/* 1795 */             if (cur.getType() == 5) {
/* 1796 */               ZRecur rrule = ((Recurrence.SimpleRepeatingRule)cur).getRule();
/* 1797 */               ParsedDateTime until = rrule.getUntil();
/* 1798 */               if (until != null) {
/* 1799 */                 seriesUntil = Math.min(until.getDateForRecurUntil(tz).getTime(), seriesUntil);
/*      */               }
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     
/* 1807 */     Set<String> obsoletedRecurIdZs = new HashSet();
/* 1808 */     if ((!isCancel) && (newInvite.isRecurrence())) {
/* 1809 */       Invite seriesInv = null;
/*      */       
/* 1811 */       long rangeStart = Long.MAX_VALUE;
/* 1812 */       long rangeEnd = Long.MIN_VALUE;
/* 1813 */       for (Invite inv : this.mInvites) {
/* 1814 */         if (inv.hasRecurId()) {
/* 1815 */           RecurId rid = inv.getRecurId();
/* 1816 */           ParsedDateTime ridDt = rid.getDt();
/* 1817 */           if (ridDt != null)
/*      */           {
/* 1819 */             if ((inv.isAllDayEvent()) && (ridDt.hasTime()) && (ridDt.hasZeroTime())) {
/* 1820 */               ParsedDateTime ridDtFixed = (ParsedDateTime)ridDt.clone();
/* 1821 */               ridDtFixed.setHasTime(false);
/* 1822 */               rid = new RecurId(ridDtFixed, rid.getRange());
/* 1823 */               ridDt = rid.getDt();
/*      */             }
/*      */             long adjustedT;
/*      */             RecurId adjustedRid;
/*      */             long adjustedT;
/* 1828 */             if (dtStartMovedBy != null) {
/* 1829 */               ParsedDateTime dt = ridDt.add(dtStartMovedBy);
/* 1830 */               RecurId adjustedRid = new RecurId(dt, rid.getRange());
/* 1831 */               adjustedT = dt.getUtcTime();
/*      */             } else {
/* 1833 */               adjustedRid = rid;
/* 1834 */               adjustedT = ridDt.getUtcTime();
/*      */             }
/* 1836 */             rangeStart = Math.min(rangeStart, adjustedT);
/* 1837 */             rangeEnd = Math.max(rangeEnd, adjustedT);
/* 1838 */             obsoletedRecurIdZs.add(adjustedRid.getDtZ());
/*      */           }
/*      */         } else {
/* 1841 */           seriesInv = inv;
/*      */         }
/*      */       }
/*      */       
/* 1845 */       long millisIn25Hours = 90000000L;
/* 1846 */       if (rangeStart != Long.MAX_VALUE)
/* 1847 */         rangeStart -= millisIn25Hours;
/* 1848 */       if (rangeEnd != Long.MIN_VALUE)
/* 1849 */         rangeEnd += millisIn25Hours;
/* 1850 */       if ((rangeStart != Long.MAX_VALUE) && (rangeEnd != Long.MIN_VALUE) && (rangeStart <= rangeEnd)) {
/* 1851 */         rangeEnd += 1L;
/* 1852 */         Recurrence.IRecurrence recur = newInvite.getRecurrence();
/* 1853 */         if ((recur instanceof Recurrence.RecurrenceRule)) {
/* 1854 */           Recurrence.RecurrenceRule rrule = (Recurrence.RecurrenceRule)recur;
/* 1855 */           List<Instance> instances = rrule.expandInstances(getId(), rangeStart, rangeEnd);
/* 1856 */           if (instances != null) {
/* 1857 */             for (Instance inst : instances) {
/* 1858 */               Invite refInv = seriesInv != null ? seriesInv : newInvite;
/* 1859 */               RecurId rid = inst.makeRecurId(refInv);
/*      */               
/* 1861 */               if ((refInv.isAllDayEvent()) && (rid.getDt() != null)) {
/* 1862 */                 ParsedDateTime ridDtFixed = (ParsedDateTime)rid.getDt().clone();
/* 1863 */                 ridDtFixed.setHasTime(false);
/* 1864 */                 rid = new RecurId(ridDtFixed, rid.getRange());
/*      */               }
/* 1866 */               obsoletedRecurIdZs.remove(rid.getDtZ());
/*      */             }
/*      */           }
/* 1869 */         } else if (recur != null)
/*      */         {
/* 1871 */           ZimbraLog.calendar.warn("Expected RecurrenceRule object, but got " + recur.getClass().getName());
/*      */         }
/*      */       }
/*      */     }
/*      */     
/* 1876 */     boolean addNewOne = true;
/* 1877 */     boolean replaceExceptionBodyWithSeriesBody = false;
/* 1878 */     boolean modifiedCalItem = false;
/* 1879 */     Invite prev = null;
/* 1880 */     ArrayList<Invite> toRemove = new ArrayList();
/* 1881 */     ArrayList<Integer> idxsToRemove = new ArrayList();
/* 1882 */     int numInvitesCurrent = this.mInvites.size();
/* 1883 */     for (int i = 0; i < numInvitesCurrent; i++) {
/* 1884 */       Invite cur = (Invite)this.mInvites.get(i);
/*      */       
/*      */ 
/* 1887 */       if ((isCancel) && (!newInvite.hasRecurId())) {
/* 1888 */         addNewOne = false;
/* 1889 */         modifiedCalItem = true;
/* 1890 */         toRemove.add(cur);
/* 1891 */         idxsToRemove.add(0, Integer.valueOf(i));
/*      */ 
/*      */       }
/*      */       else
/*      */       {
/* 1896 */         if ((!isCancel) && (cur.hasRecurId())) {
/* 1897 */           ParsedDateTime instDtStart = cur.getStartTime();
/* 1898 */           if ((instDtStart != null) && (instDtStart.getUtcTime() > seriesUntil)) {
/* 1899 */             modifiedCalItem = true;
/* 1900 */             toRemove.add(cur);
/* 1901 */             idxsToRemove.add(0, Integer.valueOf(i));
/* 1902 */             continue;
/*      */           }
/*      */         }
/*      */         
/* 1906 */         if ((cur.hasRecurId()) && (!obsoletedRecurIdZs.isEmpty())) {
/* 1907 */           RecurId rid = cur.getRecurId();
/* 1908 */           if ((rid != null) && (rid.getDt() != null))
/*      */           {
/* 1910 */             ParsedDateTime ridDt = rid.getDt();
/* 1911 */             if ((cur.isAllDayEvent()) && (ridDt.hasTime()) && (ridDt.hasZeroTime())) {
/* 1912 */               ParsedDateTime ridDtFixed = (ParsedDateTime)ridDt.clone();
/* 1913 */               ridDtFixed.setHasTime(false);
/* 1914 */               rid = new RecurId(ridDtFixed, rid.getRange());
/*      */             }
/*      */             RecurId adjustedRid;
/*      */             RecurId adjustedRid;
/* 1918 */             if (dtStartMovedBy != null) {
/* 1919 */               ParsedDateTime dt = rid.getDt().add(dtStartMovedBy);
/* 1920 */               adjustedRid = new RecurId(dt, rid.getRange());
/*      */             } else {
/* 1922 */               adjustedRid = rid;
/*      */             }
/* 1924 */             if (obsoletedRecurIdZs.contains(adjustedRid.getDtZ())) {
/* 1925 */               modifiedCalItem = true;
/* 1926 */               toRemove.add(cur);
/* 1927 */               idxsToRemove.add(0, Integer.valueOf(i));
/* 1928 */               continue;
/*      */             }
/*      */           }
/*      */         }
/*      */         
/* 1933 */         boolean matchingRecurId = recurrenceIdsMatch(cur, newInvite);
/* 1934 */         if ((discardExistingInvites) || (matchingRecurId)) {
/* 1935 */           if ((discardExistingInvites) || (newInvite.isSameOrNewerVersion(cur)))
/*      */           {
/* 1937 */             newInvite.setLocalOnly((cur.isLocalOnly()) && (newInvite.isLocalOnly()));
/*      */             
/* 1939 */             toRemove.add(cur);
/*      */             
/*      */ 
/* 1942 */             idxsToRemove.add(0, Integer.valueOf(i));
/*      */             
/* 1944 */             boolean invalidateReplies = false;
/* 1945 */             if (!discardExistingInvites) {
/* 1946 */               InviteChanges invChg = new InviteChanges(cur, newInvite);
/* 1947 */               invalidateReplies = invChg.isReplyInvalidatingChange();
/*      */             }
/* 1949 */             if ((discardExistingInvites) || (invalidateReplies))
/*      */             {
/* 1951 */               this.mReplyList.removeObsoleteEntries(newInvite.getRecurId(), newInvite.getSeqNo(), newInvite.getDTStamp());
/*      */ 
/*      */             }
/*      */             else
/*      */             {
/* 1956 */               this.mReplyList.upgradeEntriesToNewSeq(newInvite.getRecurId(), newInvite.getSeqNo(), newInvite.getDTStamp());
/*      */             }
/*      */             
/* 1959 */             prev = cur;
/* 1960 */             modifiedCalItem = true;
/* 1961 */             if ((isCancel) && (!newInvite.hasRecurId()))
/*      */             {
/* 1963 */               addNewOne = false;
/*      */             }
/*      */           }
/*      */           else
/*      */           {
/* 1968 */             return false;
/*      */           }
/* 1970 */         } else if (!isCancel) {
/* 1971 */           modifiedCalItem = true;
/*      */           
/* 1973 */           boolean addToUpdateList = false;
/* 1974 */           if (organizerChanged)
/*      */           {
/* 1976 */             cur.setOrganizer(newOrganizer);
/* 1977 */             addToUpdateList = true;
/*      */           }
/*      */           
/* 1980 */           if (needRecurrenceIdUpdate)
/*      */           {
/*      */ 
/* 1983 */             RecurId rid = cur.getRecurId();
/* 1984 */             if ((rid != null) && (rid.getDt() != null) && (oldDtStart != null)) {
/* 1985 */               ParsedDateTime ridDt = rid.getDt();
/* 1986 */               if (ridDt.sameTime(oldDtStart)) {
/* 1987 */                 ParsedDateTime dt = rid.getDt().add(dtStartMovedBy);
/* 1988 */                 RecurId newRid = new RecurId(dt, rid.getRange());
/* 1989 */                 cur.setRecurId(newRid);
/*      */                 
/*      */ 
/* 1992 */                 if (cur.isCancel()) {
/* 1993 */                   cur.setDtStart(dt);
/* 1994 */                   ParsedDateTime dtEnd = cur.getEndTime();
/* 1995 */                   if (dtEnd != null) {
/* 1996 */                     ParsedDateTime dtEndMoved = dtEnd.add(dtStartMovedBy);
/* 1997 */                     cur.setDtEnd(dtEndMoved);
/*      */                   }
/*      */                 }
/* 2000 */                 addToUpdateList = true;
/*      */               }
/*      */             }
/*      */           }
/*      */           
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 2009 */           if ((!newInvite.hasRecurId()) && (cur.hasRecurId()) && ((zcoSeriesUpdate) || (cur.isLocalOnly()))) {
/* 2010 */             if (cur.isCancel())
/*      */             {
/* 2012 */               toRemove.add(cur);
/*      */               
/*      */ 
/* 2015 */               idxsToRemove.add(0, Integer.valueOf(i));
/*      */               
/*      */ 
/* 2018 */               this.mReplyList.removeObsoleteEntries(newInvite.getRecurId(), newInvite.getSeqNo(), newInvite.getDTStamp());
/*      */               
/*      */ 
/* 2021 */               addToUpdateList = false;
/*      */             } else {
/* 2023 */               replaceExceptionBodyWithSeriesBody = true;
/*      */               
/* 2025 */               Invite copy = newInvite.newCopy();
/* 2026 */               copy.setLocalOnly(true);
/* 2027 */               copy.setMailItemId(cur.getMailItemId());
/* 2028 */               copy.setComponentNum(cur.getComponentNum());
/* 2029 */               copy.setSeqNo(cur.getSeqNo());
/* 2030 */               copy.setDtStamp(cur.getDTStamp());
/* 2031 */               copy.setRecurId(cur.getRecurId());
/* 2032 */               copy.setRecurrence(null);
/* 2033 */               ParsedDateTime start = cur.getRecurId().getDt();
/* 2034 */               if (start != null) {
/* 2035 */                 copy.setDtStart(start);
/* 2036 */                 ParsedDuration dur = cur.getDuration();
/* 2037 */                 if (dur != null) {
/* 2038 */                   copy.setDtEnd(null);
/* 2039 */                   copy.setDuration(dur);
/*      */                 } else {
/* 2041 */                   copy.setDuration(null);
/* 2042 */                   dur = cur.getEffectiveDuration();
/* 2043 */                   ParsedDateTime end = null;
/* 2044 */                   if (dur != null)
/* 2045 */                     end = start.add(dur);
/* 2046 */                   copy.setDtEnd(end);
/*      */                 }
/*      */               } else {
/* 2049 */                 copy.setDtStart(null);
/* 2050 */                 copy.setDtEnd(cur.getEndTime());
/* 2051 */                 copy.setDuration(null);
/*      */               }
/* 2053 */               copy.clearAlarms();
/* 2054 */               for (Iterator<Alarm> iter = cur.alarmsIterator(); iter.hasNext();) {
/* 2055 */                 copy.addAlarm((Alarm)iter.next());
/*      */               }
/*      */               
/*      */ 
/* 2059 */               ZAttendee me = copy.getMatchingAttendee(getAccount());
/* 2060 */               if (me != null) {
/* 2061 */                 me.setPartStat("NE");
/*      */               }
/* 2063 */               this.mInvites.set(i, copy);
/* 2064 */               addToUpdateList = true;
/*      */             }
/*      */           }
/*      */           
/* 2068 */           if (addToUpdateList)
/* 2069 */             toUpdate.add(cur);
/*      */         }
/*      */       }
/*      */     }
/* 2073 */     boolean callProcessPartStat = false;
/* 2074 */     if (addNewOne) {
/* 2075 */       newInvite.setCalendarItem(this);
/*      */       
/*      */ 
/*      */ 
/* 2079 */       if ((denyPrivateAccess) && (prev != null) && (!prev.isPublic()) && (!isCalendarResource)) {
/* 2080 */         throw ServiceException.PERM_DENIED("you do not have sufficient permissions on this calendar item");
/*      */       }
/* 2082 */       if ((prev != null) && (!newInvite.isOrganizer()) && (newInvite.sentByMe()))
/*      */       {
/*      */ 
/*      */ 
/*      */ 
/* 2087 */         newInvite.setPartStat(prev.getPartStat());
/* 2088 */         newInvite.setRsvp(prev.getRsvp());
/* 2089 */         newInvite.getCalendarItem().saveMetadata();
/*      */       }
/*      */       else
/*      */       {
/* 2093 */         callProcessPartStat = true;
/*      */       }
/*      */       
/* 2096 */       newInvite.setClassPropSetByMe(newInvite.sentByMe());
/*      */       
/*      */ 
/*      */ 
/*      */ 
/* 2101 */       if ((prev != null) && (!newInvite.isOrganizer()) && (!newInvite.sentByMe()) && 
/* 2102 */         (!prev.isPublic()) && (prev.classPropSetByMe())) {
/* 2103 */         newInvite.setClassProp(prev.getClassProp());
/* 2104 */         newInvite.setClassPropSetByMe(true);
/*      */       }
/*      */       
/*      */ 
/* 2108 */       this.mInvites.add(newInvite);
/*      */       
/*      */ 
/*      */ 
/*      */ 
/* 2113 */       this.mTzMap.add(newInvite.getTimeZoneMap());
/*      */       
/*      */ 
/*      */ 
/* 2117 */       modifiedCalItem = true;
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 2124 */     for (Iterator<Integer> iter = idxsToRemove.iterator(); iter.hasNext();) {
/* 2125 */       assert (modifiedCalItem);
/* 2126 */       Integer i = (Integer)iter.next();
/* 2127 */       this.mInvites.remove(i.intValue());
/*      */     }
/*      */     
/*      */ 
/*      */ 
/* 2132 */     int oldFlags = this.mData.getFlags();
/* 2133 */     int newFlags = this.mData.getFlags() & ((Flag.BITMASK_ATTACHED | Flag.BITMASK_DRAFT | Flag.BITMASK_HIGH_PRIORITY | Flag.BITMASK_LOW_PRIORITY) ^ 0xFFFFFFFF);
/* 2134 */     boolean hasSurvivingRequests = false;
/* 2135 */     for (Invite cur : this.mInvites) {
/* 2136 */       String method = cur.getMethod();
/* 2137 */       if ((method.equals(ZCalendar.ICalTok.REQUEST.toString())) || (method.equals(ZCalendar.ICalTok.PUBLISH.toString())))
/*      */       {
/* 2139 */         hasSurvivingRequests = true;
/* 2140 */         if (cur.hasAttachment())
/* 2141 */           newFlags |= Flag.BITMASK_ATTACHED;
/* 2142 */         if (cur.isDraft())
/* 2143 */           newFlags |= Flag.BITMASK_DRAFT;
/* 2144 */         if (cur.isHighPriority())
/* 2145 */           newFlags |= Flag.BITMASK_HIGH_PRIORITY;
/* 2146 */         if (cur.isLowPriority())
/* 2147 */           newFlags |= Flag.BITMASK_LOW_PRIORITY;
/*      */       }
/*      */     }
/* 2150 */     if (newFlags != oldFlags) {
/* 2151 */       this.mData.setFlags(newFlags);
/* 2152 */       modifiedCalItem = true;
/*      */     }
/*      */     
/* 2155 */     if (!hasSurvivingRequests) {
/* 2156 */       if (!isCancel) {
/* 2157 */         ZimbraLog.calendar.warn("Invalid state: deleting calendar item " + getId() + " in mailbox " + getMailboxId() + " while processing a non-cancel request");
/*      */       }
/*      */       else
/*      */       {
/* 2161 */         ZimbraLog.calendar.warn("Invalid state: deleting calendar item " + getId() + " in mailbox " + getMailboxId() + " because it has no invite after applying cancel invite");
/*      */       }
/*      */       
/* 2164 */       delete();
/*      */       
/* 2166 */       return false;
/*      */     }
/* 2168 */     if ((nextAlarm > 0L) && (this.mAlarmData != null) && (this.mAlarmData.getNextAtBase() != nextAlarm)) {
/* 2169 */       modifiedCalItem = true;
/*      */     }
/* 2171 */     if (modifiedCalItem) {
/* 2172 */       if ((!batch) && (!updateRecurrence(nextAlarm)))
/*      */       {
/* 2174 */         ZimbraLog.calendar.warn("Invalid state: deleting calendar item " + getId() + " in mailbox " + getMailboxId() + " because it has no invite");
/*      */         
/*      */ 
/* 2177 */         delete();
/* 2178 */         return false;
/*      */       }
/* 2180 */       if (callProcessPartStat)
/*      */       {
/*      */ 
/* 2183 */         processPartStat(newInvite, pm != null ? pm.getMimeMessage() : null, false, newInvite.getPartStat());
/*      */       }
/*      */       
/*      */ 
/*      */ 
/*      */ 
/* 2189 */       if (getFolderId() != folderId)
/*      */       {
/* 2191 */         move(folder);
/*      */       }
/*      */       
/*      */ 
/* 2195 */       boolean hadBlobPart = false;
/* 2196 */       Invite[] oldInvs = getInvites();
/* 2197 */       if (oldInvs != null) {
/* 2198 */         for (Invite oldInv : oldInvs) {
/* 2199 */           if (oldInv.hasBlobPart()) {
/* 2200 */             hadBlobPart = true;
/* 2201 */             break;
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 2207 */       boolean newInvHasBlobPart = newInvite.hasBlobPart();
/* 2208 */       if ((hadBlobPart) || (newInvHasBlobPart)) {
/* 2209 */         if (addNewOne) {
/* 2210 */           modifyBlob(toRemove, discardExistingInvites, toUpdate, pm, newInvite, isCancel, !denyPrivateAccess, true, replaceExceptionBodyWithSeriesBody);
/*      */         }
/*      */         else {
/* 2213 */           if (!newInvHasBlobPart)
/* 2214 */             toRemove.add(newInvite);
/* 2215 */           modifyBlob(toRemove, discardExistingInvites, toUpdate, null, null, isCancel, !denyPrivateAccess, true, replaceExceptionBodyWithSeriesBody);
/*      */         }
/*      */         
/*      */       }
/*      */       else
/*      */       {
/* 2221 */         markItemModified(131072);
/*      */         try {
/* 2223 */           if (batch) {
/* 2224 */             this.persistBatchedChanges = true;
/*      */           }
/*      */           else
/*      */           {
/* 2228 */             setContent(null, null);
/*      */           }
/*      */         } catch (IOException e) {
/* 2231 */           throw ServiceException.FAILURE("IOException", e);
/*      */         }
/*      */       }
/*      */       
/* 2235 */       Invite defInvite = getDefaultInviteOrNull();
/* 2236 */       if (defInvite != null) {
/* 2237 */         Collection<Instance> instances = expandInstances(0L, Long.MAX_VALUE, false);
/* 2238 */         if (instances.isEmpty()) {
/* 2239 */           ZimbraLog.calendar.warn("Deleting calendar item " + getId() + " in mailbox " + getMailboxId() + " because it has no invite after applying request/cancel invite");
/*      */           
/* 2241 */           delete();
/* 2242 */           return true;
/*      */         }
/*      */       }
/*      */       
/* 2246 */       Callback cb = getCallback();
/* 2247 */       if (cb != null) {
/* 2248 */         cb.modified(this);
/*      */       }
/* 2250 */       return true;
/*      */     }
/*      */     
/* 2253 */     if (getFolderId() != folderId)
/*      */     {
/* 2255 */       move(folder);
/*      */     }
/* 2257 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */   void delete()
/*      */     throws ServiceException
/*      */   {
/* 2264 */     super.delete();
/* 2265 */     if (!inDumpster()) {
/* 2266 */       Callback cb = getCallback();
/* 2267 */       if (cb != null) {
/* 2268 */         cb.deleted(this);
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 2278 */   private static Callback sCallback = null;
/*      */   
/* 2280 */   public static synchronized void registerCallback(Callback cb) { sCallback = cb; }
/*      */   
/*      */   public static synchronized Callback getCallback() {
/* 2283 */     return sCallback;
/*      */   }
/*      */   
/*      */   private String calDesc(Invite invite) {
/* 2287 */     return invite.isTodo() ? "a task" : "an appointment";
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean organizerChangeCheck(Invite newInvite, boolean denyChange)
/*      */     throws ServiceException
/*      */   {
/* 2297 */     Invite originalInvite = null;
/* 2298 */     if (!newInvite.hasRecurId())
/*      */     {
/* 2300 */       originalInvite = getDefaultInviteOrNull();
/*      */     }
/*      */     else {
/* 2303 */       boolean found = false;
/* 2304 */       RecurId newRid = newInvite.getRecurId();
/* 2305 */       for (Invite inv : this.mInvites) {
/* 2306 */         if ((inv.hasRecurId()) && (newRid.equals(inv.getRecurId()))) {
/* 2307 */           originalInvite = inv;
/* 2308 */           found = true;
/* 2309 */           break;
/*      */         }
/*      */       }
/* 2312 */       if (!found)
/*      */       {
/* 2314 */         originalInvite = getDefaultInviteOrNull();
/*      */       }
/*      */     }
/* 2317 */     if (originalInvite == null)
/*      */     {
/* 2319 */       if (this.mInvites.size() > 0)
/* 2320 */         originalInvite = (Invite)this.mInvites.get(0);
/* 2321 */       if (originalInvite == null)
/*      */       {
/*      */ 
/* 2324 */         return false;
/*      */       }
/*      */     }
/* 2327 */     boolean updatingSameComponent = true;
/* 2328 */     if (newInvite.hasRecurId()) {
/* 2329 */       if (originalInvite.hasRecurId()) {
/* 2330 */         updatingSameComponent = newInvite.getRecurId().equals(originalInvite.getRecurId());
/*      */       } else {
/* 2332 */         updatingSameComponent = false;
/*      */       }
/*      */     }
/*      */     
/* 2336 */     boolean changed = false;
/* 2337 */     ZOrganizer originalOrganizer = originalInvite.getOrganizer();
/* 2338 */     if (!originalInvite.isOrganizer())
/*      */     {
/* 2340 */       if (newInvite.hasOrganizer()) {
/* 2341 */         String newOrgAddr = newInvite.getOrganizer().getAddress();
/* 2342 */         if (originalOrganizer == null) {
/* 2343 */           if (denyChange) {
/* 2344 */             newInvite.isTodo();
/* 2345 */             if (updatingSameComponent) {
/* 2346 */               throw BadOrganizerException.ADD_ORGANIZER_NOT_ALLOWED(newOrgAddr, calDesc(newInvite));
/*      */             }
/* 2348 */             throw BadOrganizerException.ORGANIZER_INTRODUCED_FOR_EXCEPTION(newOrgAddr, calDesc(newInvite));
/*      */           }
/*      */           
/*      */ 
/* 2352 */           changed = true;
/*      */         }
/*      */         else
/*      */         {
/* 2356 */           String origOrgAddr = originalOrganizer.getAddress();
/* 2357 */           if ((newOrgAddr == null) || (!CalendarUtils.belongToSameAccount(origOrgAddr, newOrgAddr))) {
/* 2358 */             if (denyChange) {
/* 2359 */               if (updatingSameComponent) {
/* 2360 */                 throw BadOrganizerException.CHANGE_ORGANIZER_NOT_ALLOWED(origOrgAddr, newOrgAddr, calDesc(newInvite));
/*      */               }
/*      */               
/* 2363 */               throw BadOrganizerException.DIFF_ORGANIZER_IN_COMPONENTS(origOrgAddr, newOrgAddr, calDesc(newInvite));
/*      */             }
/*      */             
/*      */ 
/* 2367 */             changed = true;
/*      */           }
/*      */         }
/*      */       }
/* 2371 */       else if (originalOrganizer != null)
/*      */       {
/* 2373 */         String origOrgAddr = originalOrganizer.getAddress();
/* 2374 */         if (denyChange) {
/* 2375 */           if (updatingSameComponent) {
/* 2376 */             throw BadOrganizerException.DEL_ORGANIZER_NOT_ALLOWED(origOrgAddr, calDesc(newInvite));
/*      */           }
/* 2378 */           throw BadOrganizerException.MISSING_ORGANIZER_IN_SINGLE_INSTANCE(origOrgAddr, calDesc(newInvite));
/*      */         }
/*      */         
/*      */ 
/* 2382 */         changed = true;
/*      */ 
/*      */       }
/*      */       
/*      */ 
/*      */     }
/* 2388 */     else if ((newInvite.hasOrganizer()) && 
/* 2389 */       (!newInvite.isOrganizer())) {
/* 2390 */       String newOrgAddr = newInvite.getOrganizer().getAddress();
/* 2391 */       String origOrgAddr = originalOrganizer != null ? originalOrganizer.getAddress() : null;
/* 2392 */       if (newOrgAddr.equalsIgnoreCase(origOrgAddr))
/*      */       {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 2398 */         newInvite.setIsOrganizer(true);
/*      */       }
/* 2400 */       if (!newInvite.isOrganizer()) {
/* 2401 */         if (denyChange) {
/* 2402 */           if (originalOrganizer != null) {
/* 2403 */             if (updatingSameComponent) {
/* 2404 */               throw BadOrganizerException.CHANGE_ORGANIZER_NOT_ALLOWED(origOrgAddr, newOrgAddr, calDesc(newInvite));
/*      */             }
/*      */             
/* 2407 */             throw BadOrganizerException.DIFF_ORGANIZER_IN_COMPONENTS(origOrgAddr, newOrgAddr, calDesc(newInvite));
/*      */           }
/*      */           
/*      */ 
/* 2411 */           if (updatingSameComponent) {
/* 2412 */             throw BadOrganizerException.ADD_ORGANIZER_NOT_ALLOWED(newOrgAddr, calDesc(newInvite));
/*      */           }
/* 2414 */           throw BadOrganizerException.ORGANIZER_INTRODUCED_FOR_EXCEPTION(newOrgAddr, calDesc(newInvite));
/*      */         }
/*      */         
/*      */ 
/*      */ 
/* 2419 */         changed = true;
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/* 2425 */     if (changed) {
/* 2426 */       String origOrg = originalOrganizer != null ? originalOrganizer.getAddress() : null;
/* 2427 */       ZOrganizer newOrganizer = newInvite.getOrganizer();
/* 2428 */       String newOrg = newOrganizer != null ? newOrganizer.getAddress() : null;
/* 2429 */       boolean wasOrganizer = originalInvite.isOrganizer();
/* 2430 */       boolean isOrganizer = newInvite.isOrganizer();
/* 2431 */       ZimbraLog.calendar.info("Changed organizer: old=" + origOrg + ", new=" + newOrg + ", wasOrg=" + wasOrganizer + ", isOrg=" + isOrganizer + ", UID=\"" + newInvite.getUid() + "\", invId=" + newInvite.getMailItemId());
/*      */     }
/*      */     
/*      */ 
/* 2435 */     return changed;
/*      */   }
/*      */   
/*      */   public static abstract interface Callback { public abstract void created(CalendarItem paramCalendarItem) throws ServiceException;
/*      */     
/*      */     public abstract void modified(CalendarItem paramCalendarItem) throws ServiceException;
/*      */     
/*      */     public abstract void deleted(CalendarItem paramCalendarItem) throws ServiceException; }
/*      */   
/*      */   private static class PMDataSource implements DataSource { private final ParsedMessage mPm;
/*      */     
/* 2446 */     public PMDataSource(ParsedMessage pm) { this.mPm = pm; }
/*      */     
/*      */ 
/*      */ 
/*      */     public String getName()
/*      */     {
/* 2452 */       return this.mPm != null ? this.mPm.getMessageID() : null;
/*      */     }
/*      */     
/*      */     public String getContentType()
/*      */     {
/* 2457 */       return "message/rfc822";
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     public InputStream getInputStream()
/*      */       throws IOException
/*      */     {
/* 2469 */       InputStream is = null;
/* 2470 */       if (this.mPm != null)
/* 2471 */         is = this.mPm.getRawInputStream();
/* 2472 */       if (is == null)
/* 2473 */         is = new ByteArrayInputStream(new byte[0]);
/* 2474 */       return is;
/*      */     }
/*      */     
/*      */     public OutputStream getOutputStream()
/*      */     {
/* 2479 */       throw new UnsupportedOperationException();
/*      */     }
/*      */   }
/*      */   
/*      */   private MailboxBlob storeUpdatedBlob(MimeMessage mm) throws ServiceException, IOException {
/* 2484 */     ParsedMessage pm = new ParsedMessage(mm, this.mMailbox.attachmentsIndexingEnabled());
/* 2485 */     StoreManager sm = StoreManager.getInstance();
/* 2486 */     InputStream is = null;
/*      */     try {
/* 2488 */       is = pm.getRawInputStream();
/* 2489 */       StagedBlob sblob; if (is != null) {
/* 2490 */         sblob = sm.stage(is, this.mMailbox);
/* 2491 */         return setContent(sblob, pm);
/*      */       }
/* 2493 */       ZimbraLog.calendar.warn("Invalid state: updating blob with null data for calendar item " + getId() + " in mailbox " + getMailboxId());
/*      */       
/*      */ 
/* 2496 */       return setContent(null, pm);
/*      */     }
/*      */     finally {
/* 2499 */       ByteUtil.closeStream(is);
/*      */     }
/*      */   }
/*      */   
/*      */   void reanalyze(Object data, long newSize) throws ServiceException
/*      */   {
/* 2505 */     String subject = null;
/* 2506 */     Invite firstInvite = getDefaultInviteOrNull();
/* 2507 */     if (firstInvite != null) {
/* 2508 */       subject = firstInvite.getName();
/*      */     }
/* 2510 */     this.mData.setSubject(Strings.nullToEmpty(subject));
/* 2511 */     saveData(new DbMailItem(this.mMailbox));
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private MailboxBlob createBlob(ParsedMessage invPm, Invite firstInvite)
/*      */     throws ServiceException
/*      */   {
/* 2525 */     if ((!firstInvite.hasAttachment()) && ((invPm == null) || (firstInvite.descInMeta())))
/*      */     {
/*      */ 
/*      */ 
/*      */ 
/* 2530 */       saveMetadata();
/* 2531 */       return null;
/*      */     }
/*      */     
/*      */     try
/*      */     {
/* 2536 */       MimeMessage mm = new Mime.FixedMimeMessage(JMSession.getSession());
/* 2537 */       MimeMultipart mmp = new ZMimeMultipart("digest");
/* 2538 */       mm.setContent(mmp);
/*      */       
/*      */ 
/* 2541 */       MimeBodyPart mbp = new ZMimeBodyPart();
/* 2542 */       mbp.setDataHandler(new DataHandler(new PMDataSource(invPm)));
/* 2543 */       mbp.addHeader("invId", Integer.toString(firstInvite.getMailItemId()));
/* 2544 */       mmp.addBodyPart(mbp);
/*      */       
/* 2546 */       mm.saveChanges();
/*      */       
/* 2548 */       return storeUpdatedBlob(mm);
/*      */     } catch (MessagingException e) {
/* 2550 */       throw ServiceException.FAILURE("MessagingException " + e, e);
/*      */     } catch (IOException e) {
/* 2552 */       throw ServiceException.FAILURE("IOException " + e, e);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private void modifyBlob(List<Invite> toRemove, boolean removeAllExistingInvites, List<Invite> toUpdate, ParsedMessage invPm, Invite newInv, boolean isCancel, boolean allowPrivateAccess, boolean forceSave, boolean replaceExceptionBodyWithSeriesBody)
/*      */     throws ServiceException
/*      */   {
/*      */     try
/*      */     {
/* 2591 */       MimeMessage mm = null;
/* 2592 */       if (getSize() > 0L) {
/*      */         try {
/* 2594 */           mm = getMimeMessage();
/*      */         } catch (ServiceException e) {
/* 2596 */           ZimbraLog.calendar.warn("Error reading blob for calendar item " + getId() + " in mailbox " + getMailboxId(), e);
/*      */         }
/*      */       }
/*      */       
/* 2600 */       if (mm == null) {
/* 2601 */         if ((newInv != null) && (invPm != null))
/*      */         {
/*      */ 
/* 2604 */           createBlob(invPm, newInv);
/*      */         }
/* 2606 */         else if (forceSave) {
/* 2607 */           saveMetadata();
/*      */         }
/* 2609 */         return;
/*      */       }
/*      */       
/*      */ 
/*      */ 
/* 2614 */       Object obj = mm.getContent();
/* 2615 */       MimeMultipart mmp; if ((obj instanceof MimeMultipart)) {
/* 2616 */         mmp = (MimeMultipart)obj;
/*      */       } else {
/* 2618 */         throw ServiceException.FAILURE("Expected MimeMultipart, but got " + obj.getClass().getName() + ": id=" + this.mId + ", content=" + obj.toString(), null);
/*      */       }
/*      */       
/*      */       MimeMultipart mmp;
/* 2622 */       boolean updated = false;
/*      */       
/*      */ 
/* 2625 */       if (removeAllExistingInvites) {
/* 2626 */         int numParts = mmp.getCount();
/* 2627 */         if (numParts > 0) {
/* 2628 */           for (int i = numParts - 1; i >= 0; i--) {
/* 2629 */             mmp.removeBodyPart(i);
/*      */           }
/* 2631 */           updated = true;
/*      */         }
/*      */       }
/*      */       else {
/* 2635 */         for (Invite inv : toRemove)
/*      */         {
/*      */           int matchedIdx;
/*      */           do {
/* 2639 */             int numParts = mmp.getCount();
/* 2640 */             matchedIdx = -1;
/* 2641 */             for (int i = 0; i < numParts; i++) {
/* 2642 */               MimeBodyPart mbp = (MimeBodyPart)mmp.getBodyPart(i);
/* 2643 */               String[] hdrs = mbp.getHeader("invId");
/* 2644 */               if ((hdrs == null) || (hdrs.length == 0)) {
/* 2645 */                 matchedIdx = i;
/* 2646 */                 break;
/*      */               }
/* 2648 */               if ((hdrs != null) && (hdrs.length > 0) && (Integer.parseInt(hdrs[0]) == inv.getMailItemId())) {
/* 2649 */                 matchedIdx = i;
/* 2650 */                 break;
/*      */               }
/*      */             }
/* 2653 */             if (matchedIdx > -1) {
/* 2654 */               mmp.removeBodyPart(matchedIdx);
/* 2655 */               updated = true;
/*      */             }
/* 2657 */           } while (matchedIdx > -1);
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 2662 */       for (Invite inv : toUpdate) {
/* 2663 */         MimeBodyPart mbpInv = null;
/* 2664 */         int numParts = mmp.getCount();
/* 2665 */         for (int i = 0; i < numParts; i++) {
/* 2666 */           MimeBodyPart mbp = (MimeBodyPart)mmp.getBodyPart(i);
/* 2667 */           String[] hdrs = mbp.getHeader("invId");
/* 2668 */           if ((hdrs != null) && (hdrs.length > 0) && (Integer.parseInt(hdrs[0]) == inv.getMailItemId()))
/*      */           {
/* 2670 */             mbpInv = mbp;
/* 2671 */             break;
/*      */           }
/*      */         }
/* 2674 */         if (mbpInv != null)
/*      */         {
/*      */ 
/* 2677 */           if (replaceExceptionBodyWithSeriesBody)
/*      */           {
/* 2679 */             mmp.removeBodyPart(mbpInv);
/* 2680 */             mbpInv = new ZMimeBodyPart();
/* 2681 */             mbpInv.setDataHandler(new DataHandler(new PMDataSource(invPm)));
/* 2682 */             mbpInv.addHeader("invId", Integer.toString(inv.getMailItemId()));
/* 2683 */             mmp.addBodyPart(mbpInv);
/* 2684 */             mm.saveChanges();
/*      */           }
/*      */           
/*      */ 
/* 2688 */           String mbpInvCt = mbpInv.getContentType();
/* 2689 */           Object objInv = mbpInv.getContent();
/* 2690 */           if ((objInv instanceof MimeMessage)) {
/* 2691 */             MimeMessage mmInv = (MimeMessage)objInv;
/* 2692 */             Object objMulti = mmInv.getContent();
/* 2693 */             if ((objMulti instanceof MimeMultipart)) {
/* 2694 */               MimeMultipart multi = (MimeMultipart)objMulti;
/* 2695 */               int numSubParts = multi.getCount();
/* 2696 */               int icalPartNum = -1;
/* 2697 */               for (int j = 0; j < numSubParts; j++) {
/* 2698 */                 MimeBodyPart part = (MimeBodyPart)multi.getBodyPart(j);
/* 2699 */                 ContentType ct = new ContentType(part.getContentType());
/* 2700 */                 if (ct.match("text/calendar")) {
/* 2701 */                   icalPartNum = j;
/* 2702 */                   break;
/*      */                 }
/*      */               }
/* 2705 */               if (icalPartNum != -1) {
/* 2706 */                 updated = true;
/* 2707 */                 multi.removeBodyPart(icalPartNum);
/* 2708 */                 ZCalendar.ZVCalendar cal = inv.newToICalendar(allowPrivateAccess);
/* 2709 */                 MimeBodyPart icalPart = CalendarMailSender.makeICalIntoMimePart(cal);
/* 2710 */                 multi.addBodyPart(icalPart, icalPartNum);
/*      */                 
/*      */ 
/* 2713 */                 mmInv.setContent(multi);
/* 2714 */                 mmInv.saveChanges();
/* 2715 */                 mbpInv.setContent(mmInv, mbpInvCt);
/*      */               }
/*      */             }
/*      */           }
/*      */         } }
/* 2720 */       if (newInv != null) {
/* 2721 */         MimeBodyPart mbp = new ZMimeBodyPart();
/* 2722 */         mbp.setDataHandler(new DataHandler(new PMDataSource(invPm)));
/* 2723 */         mmp.addBodyPart(mbp);
/* 2724 */         mbp.addHeader("invId", Integer.toString(newInv.getMailItemId()));
/* 2725 */         updated = true;
/*      */       }
/*      */       
/* 2728 */       if (!updated) {
/* 2729 */         if (forceSave)
/* 2730 */           saveMetadata();
/* 2731 */         return;
/*      */       }
/*      */       
/* 2734 */       if (mmp.getCount() == 0) {
/* 2735 */         markBlobForDeletion();
/* 2736 */         setContent(null, null);
/* 2737 */         if (forceSave) {
/* 2738 */           saveMetadata();
/*      */         }
/*      */       } else {
/* 2741 */         mm.setContent(mmp);
/* 2742 */         mm.saveChanges();
/* 2743 */         storeUpdatedBlob(mm);
/*      */       }
/*      */     } catch (MessagingException e) {
/* 2746 */       throw ServiceException.FAILURE("MessagingException", e);
/*      */     } catch (IOException e) {
/* 2748 */       throw ServiceException.FAILURE("IOException", e);
/*      */     }
/*      */   }
/*      */   
/*      */   public static class ReplyInfo
/*      */   {
/*      */     private ZAttendee mAttendee;
/*      */     private int mSeqNo;
/*      */     private long mDtStamp;
/*      */     private final RecurId mRecurId;
/*      */     private static final String FN_RECURID = "r";
/*      */     private static final String FN_SEQNO = "s";
/*      */     private static final String FN_DTSTAMP = "d";
/*      */     private static final String FN_ATTENDEE = "at";
/*      */     
/*      */     public ReplyInfo(ZAttendee at, int seq, long dtstamp, RecurId recurId) {
/* 2764 */       this.mAttendee = at;
/* 2765 */       this.mSeqNo = seq;
/* 2766 */       this.mDtStamp = dtstamp;
/* 2767 */       this.mRecurId = recurId;
/*      */     }
/*      */     
/* 2770 */     public ZAttendee getAttendee() { return this.mAttendee; }
/* 2771 */     public int getSeq() { return this.mSeqNo; }
/* 2772 */     public long getDtStamp() { return this.mDtStamp; }
/* 2773 */     public RecurId getRecurId() { return this.mRecurId; }
/*      */     
/*      */     public Metadata encodeAsMetadata() {
/* 2776 */       Metadata meta = new Metadata();
/*      */       
/* 2778 */       if (this.mRecurId != null) {
/* 2779 */         meta.put("r", this.mRecurId.encodeMetadata());
/*      */       }
/*      */       
/* 2782 */       meta.put("s", this.mSeqNo);
/* 2783 */       meta.put("d", this.mDtStamp);
/* 2784 */       meta.put("at", this.mAttendee.encodeAsMetadata());
/*      */       
/* 2786 */       return meta;
/*      */     }
/*      */     
/*      */     public static ReplyInfo decodeFromMetadata(Metadata md, TimeZoneMap tzMap) throws ServiceException {
/*      */       RecurId recurId;
/*      */       RecurId recurId;
/* 2792 */       if (md.containsKey("r")) {
/* 2793 */         recurId = RecurId.decodeMetadata(md.getMap("r"), tzMap);
/*      */       } else {
/* 2795 */         recurId = null;
/*      */       }
/* 2797 */       int seq = (int)md.getLong("s");
/* 2798 */       long dtstamp = md.getLong("d");
/* 2799 */       Metadata metaAttendee = md.getMap("at");
/* 2800 */       ZAttendee at = metaAttendee != null ? new ZAttendee(metaAttendee) : null;
/* 2801 */       ReplyInfo ri = new ReplyInfo(at, seq, dtstamp, recurId);
/* 2802 */       return ri;
/*      */     }
/*      */     
/*      */     public CalendarReply toJAXB() {
/* 2806 */       ZAttendee attendee = getAttendee();
/* 2807 */       CalendarReply jaxb = new CalendarReply(getSeq(), getDtStamp(), attendee.getAddress());
/* 2808 */       if (attendee.hasSentBy()) {
/* 2809 */         jaxb.setSentBy(attendee.getSentBy());
/*      */       }
/* 2811 */       if (attendee.hasPartStat()) {
/* 2812 */         jaxb.setPartStat(attendee.getPartStat());
/*      */       }
/* 2814 */       RecurId rid = getRecurId();
/* 2815 */       if (rid != null) {
/* 2816 */         jaxb.setRecurrenceRangeType(rid.getRange());
/* 2817 */         ParsedDateTime dateTime = rid.getDt();
/* 2818 */         jaxb.setRecurrenceId(dateTime.getDateTimePartString(false));
/* 2819 */         if (dateTime.hasTime()) {
/* 2820 */           jaxb.setTimezone(dateTime.getTZName());
/* 2821 */           jaxb.setRecurIdZ(rid.getDtZ());
/*      */         }
/*      */       }
/* 2824 */       return jaxb;
/*      */     }
/*      */     
/*      */     public String toString()
/*      */     {
/* 2829 */       StringBuilder sb = new StringBuilder();
/* 2830 */       sb.append("seq=").append(this.mSeqNo);
/* 2831 */       sb.append(", dtstamp=").append(this.mDtStamp);
/* 2832 */       if (this.mRecurId != null)
/* 2833 */         sb.append(", recurId=\"").append(this.mRecurId).append("\"");
/* 2834 */       sb.append(", attendee=").append(this.mAttendee);
/* 2835 */       return sb.toString();
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   protected static class ReplyList
/*      */   {
/*      */     List<CalendarItem.ReplyInfo> mReplies;
/*      */     
/*      */     private static final String FN_NUM_REPLY_INFO = "n";
/*      */     
/*      */     private static final String FN_REPLY_INFO = "i";
/*      */     
/*      */     public String toString()
/*      */     {
/* 2850 */       StringBuilder sb = new StringBuilder();
/* 2851 */       sb.append("[ReplyList]\n");
/* 2852 */       if (this.mReplies != null) {
/* 2853 */         for (CalendarItem.ReplyInfo ri : this.mReplies) {
/* 2854 */           sb.append(ri.toString()).append("\n");
/*      */         }
/*      */       }
/* 2857 */       sb.append("[end]\n");
/* 2858 */       return sb.toString();
/*      */     }
/*      */     
/*      */     public ReplyList() {
/* 2862 */       this.mReplies = new ArrayList();
/*      */     }
/*      */     
/*      */     public ReplyList(List<CalendarItem.ReplyInfo> list) {
/* 2866 */       this.mReplies = list;
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */     Metadata encodeAsMetadata()
/*      */     {
/* 2873 */       Metadata meta = new Metadata();
/*      */       
/* 2875 */       meta.put("n", this.mReplies.size());
/* 2876 */       for (int i = 0; i < this.mReplies.size(); i++) {
/* 2877 */         String fn = "i" + i;
/*      */         
/* 2879 */         meta.put(fn, ((CalendarItem.ReplyInfo)this.mReplies.get(i)).encodeAsMetadata());
/*      */       }
/* 2881 */       return meta;
/*      */     }
/*      */     
/*      */     static ReplyList decodeFromMetadata(Metadata md, TimeZoneMap tzMap) throws ServiceException {
/* 2885 */       ReplyList toRet = new ReplyList();
/* 2886 */       int numReplies = (int)md.getLong("n");
/* 2887 */       toRet.mReplies = new ArrayList(numReplies);
/* 2888 */       for (int i = 0; i < numReplies; i++) {
/* 2889 */         CalendarItem.ReplyInfo inf = CalendarItem.ReplyInfo.decodeFromMetadata(md.getMap("i" + i), tzMap);
/* 2890 */         toRet.mReplies.add(i, inf);
/*      */       }
/* 2892 */       return toRet;
/*      */     }
/*      */     
/*      */     void removeObsoleteEntries(RecurId recurId, int seqNo, long dtStamp) {
/* 2896 */       for (Iterator<CalendarItem.ReplyInfo> iter = this.mReplies.iterator(); iter.hasNext();) {
/* 2897 */         CalendarItem.ReplyInfo cur = (CalendarItem.ReplyInfo)iter.next();
/*      */         
/* 2899 */         if (recurMatches(CalendarItem.ReplyInfo.access$100(cur), recurId))
/*      */         {
/* 2901 */           if (CalendarItem.ReplyInfo.access$700(cur) < seqNo) {
/* 2902 */             iter.remove();
/*      */           }
/* 2904 */         } else if (recurId == null)
/*      */         {
/*      */ 
/* 2907 */           iter.remove();
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */     void upgradeEntriesToNewSeq(RecurId recurId, int seqNo, long dtStamp) {
/* 2913 */       List<CalendarItem.ReplyInfo> upgraded = new ArrayList();
/* 2914 */       for (Iterator<CalendarItem.ReplyInfo> iter = this.mReplies.iterator(); iter.hasNext();) {
/* 2915 */         CalendarItem.ReplyInfo cur = (CalendarItem.ReplyInfo)iter.next();
/* 2916 */         if ((recurMatches(CalendarItem.ReplyInfo.access$100(cur), recurId)) && (
/* 2917 */           (CalendarItem.ReplyInfo.access$700(cur) < seqNo) || ((CalendarItem.ReplyInfo.access$700(cur) == seqNo) && (CalendarItem.ReplyInfo.access$800(cur) < dtStamp))))
/*      */         {
/* 2919 */           iter.remove();
/* 2920 */           CalendarItem.ReplyInfo reply = new CalendarItem.ReplyInfo(cur.getAttendee(), seqNo, dtStamp, recurId);
/* 2921 */           upgraded.add(reply);
/*      */         }
/*      */       }
/*      */       
/* 2925 */       this.mReplies.addAll(upgraded);
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     boolean maybeStoreNewReply(Invite inv, ZAttendee at, CalendarItem calendarItem)
/*      */       throws ServiceException
/*      */     {
/* 2936 */       AccountUtil.AccountAddressMatcher acctMatcher = null;
/* 2937 */       String address = at.getAddress();
/* 2938 */       if (address != null) {
/* 2939 */         Account acct = Provisioning.getInstance().get(Key.AccountBy.name, address);
/* 2940 */         if (acct != null) {
/* 2941 */           acctMatcher = new AccountUtil.AccountAddressMatcher(acct);
/*      */         }
/*      */       }
/*      */       
/* 2945 */       for (Iterator<CalendarItem.ReplyInfo> iter = this.mReplies.iterator(); iter.hasNext();) {
/* 2946 */         CalendarItem.ReplyInfo cur = (CalendarItem.ReplyInfo)iter.next();
/* 2947 */         if ((at.addressesMatch(CalendarItem.ReplyInfo.access$900(cur))) || ((acctMatcher != null) && (acctMatcher.matches(CalendarItem.ReplyInfo.access$900(cur).getAddress()))))
/*      */         {
/* 2949 */           if (recurMatches(inv.getRecurId(), CalendarItem.ReplyInfo.access$100(cur))) {
/* 2950 */             if (inv.getSeqNo() < cur.getSeq())
/* 2951 */               return false;
/* 2952 */             if ((inv.getSeqNo() == cur.getSeq()) && (inv.getDTStamp() < cur.getDtStamp())) {
/* 2953 */               return false;
/*      */             }
/* 2955 */             iter.remove();
/* 2956 */             CalendarItem.ReplyInfo toAdd = new CalendarItem.ReplyInfo(at, inv.getSeqNo(), inv.getDTStamp(), inv.getRecurId());
/* 2957 */             calendarItem.markItemModified(131072);
/* 2958 */             this.mReplies.add(toAdd);
/* 2959 */             return true;
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 2965 */       CalendarItem.ReplyInfo toAdd = new CalendarItem.ReplyInfo(at, inv.getSeqNo(), inv.getDTStamp(), inv.getRecurId());
/* 2966 */       calendarItem.markItemModified(131072);
/* 2967 */       this.mReplies.add(toAdd);
/* 2968 */       return true;
/*      */     }
/*      */     
/*      */     void modifyPartStat(Account acctOrNull, RecurId recurId, String cnStr, String addressStr, String cutypeStr, String roleStr, String partStatStr, Boolean needsReply, int seqNo, long dtStamp) throws ServiceException
/*      */     {
/* 2973 */       AccountUtil.AccountAddressMatcher acctMatcher = acctOrNull != null ? new AccountUtil.AccountAddressMatcher(acctOrNull) : null;
/* 2974 */       for (CalendarItem.ReplyInfo cur : this.mReplies) {
/* 2975 */         if (((CalendarItem.ReplyInfo.access$100(cur) == null) && (recurId == null)) || ((CalendarItem.ReplyInfo.access$100(cur) != null) && (CalendarItem.ReplyInfo.access$100(cur).withinRange(recurId)) && (
/* 2976 */           ((acctMatcher != null) && (acctMatcher.matches(CalendarItem.ReplyInfo.access$900(cur).getAddress()))) || ((acctMatcher == null) && (CalendarItem.ReplyInfo.access$900(cur).addressMatches(addressStr))))))
/*      */         {
/* 2978 */           if (CalendarItem.ReplyInfo.access$900(cur).hasCn()) {
/* 2979 */             cnStr = CalendarItem.ReplyInfo.access$900(cur).getCn();
/*      */           }
/*      */           
/* 2982 */           if (CalendarItem.ReplyInfo.access$900(cur).hasCUType()) {
/* 2983 */             cutypeStr = CalendarItem.ReplyInfo.access$900(cur).getCUType();
/*      */           }
/*      */           
/* 2986 */           if (CalendarItem.ReplyInfo.access$900(cur).hasRole()) {
/* 2987 */             roleStr = CalendarItem.ReplyInfo.access$900(cur).getRole();
/*      */           }
/*      */           
/* 2990 */           ZAttendee newAt = new ZAttendee(CalendarItem.ReplyInfo.access$900(cur));
/* 2991 */           newAt.setCn(cnStr);
/* 2992 */           newAt.setCUType(cutypeStr);
/* 2993 */           newAt.setRole(roleStr);
/* 2994 */           newAt.setPartStat(partStatStr);
/* 2995 */           newAt.setRsvp(needsReply);
/*      */           
/* 2997 */           CalendarItem.ReplyInfo.access$902(cur, newAt);
/* 2998 */           CalendarItem.ReplyInfo.access$702(cur, seqNo);
/* 2999 */           CalendarItem.ReplyInfo.access$802(cur, dtStamp);
/* 3000 */           return;
/*      */         }
/*      */       }
/*      */       
/*      */ 
/*      */ 
/* 3006 */       ZAttendee at = new ZAttendee(addressStr, cnStr, null, null, null, cutypeStr, roleStr, partStatStr, needsReply, null, null, null, null);
/*      */       
/*      */ 
/*      */ 
/* 3010 */       CalendarItem.ReplyInfo ri = new CalendarItem.ReplyInfo(at, seqNo, dtStamp, recurId);
/* 3011 */       this.mReplies.add(ri);
/*      */     }
/*      */     
/*      */     boolean recurMatches(RecurId lhs, RecurId rhs) {
/* 3015 */       if (lhs == null) {
/* 3016 */         return rhs == null;
/*      */       }
/* 3018 */       return lhs.equals(rhs);
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */     ZAttendee getEffectiveAttendee(Account acct, Invite inv, CalendarItem.Instance inst)
/*      */       throws ServiceException
/*      */     {
/* 3037 */       boolean isSimple = (inv.getRecurrence() == null) && (!inv.hasRecurId());
/* 3038 */       ZAttendee defaultAt = null;
/*      */       
/* 3040 */       AccountUtil.AccountAddressMatcher acctMatcher = new AccountUtil.AccountAddressMatcher(acct);
/* 3041 */       for (CalendarItem.ReplyInfo cur : this.mReplies) {
/* 3042 */         if (acctMatcher.matches(CalendarItem.ReplyInfo.access$900(cur).getAddress()))
/*      */         {
/*      */ 
/*      */ 
/* 3046 */           boolean match = (CalendarItem.ReplyInfo.access$100(cur) == null) && ((inst == null) || (isSimple));
/* 3047 */           if ((!match) && 
/* 3048 */             (inst != null) && (CalendarItem.ReplyInfo.access$100(cur) != null)) {
/* 3049 */             long instStart = inst.getStart();
/* 3050 */             if (CalendarItem.Instance.access$1000(inst) != null) {
/* 3051 */               RecurId instRecurId = CalendarItem.Instance.access$1000(inst).getRecurrenceId();
/* 3052 */               if (instRecurId != null)
/*      */               {
/* 3054 */                 instStart = instRecurId.getDt().getUtcTime();
/*      */               }
/*      */             }
/* 3057 */             match = CalendarItem.ReplyInfo.access$100(cur).withinRange(instStart);
/*      */           }
/*      */           
/* 3060 */           if (match)
/*      */           {
/*      */ 
/*      */ 
/*      */ 
/* 3065 */             if ((inv.getSeqNo() <= CalendarItem.ReplyInfo.access$700(cur)) && 
/* 3066 */               (inv.getDTStamp() <= CalendarItem.ReplyInfo.access$800(cur))) {
/* 3067 */               return CalendarItem.ReplyInfo.access$900(cur);
/*      */             }
/*      */           }
/*      */           
/*      */ 
/* 3072 */           if (CalendarItem.ReplyInfo.access$100(cur) == null)
/*      */           {
/* 3074 */             if ((inv.getSeqNo() <= CalendarItem.ReplyInfo.access$700(cur)) && (inv.getDTStamp() <= CalendarItem.ReplyInfo.access$800(cur))) {
/* 3075 */               defaultAt = CalendarItem.ReplyInfo.access$900(cur);
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 3082 */       if ((isSimple) || (inst == null))
/*      */       {
/* 3084 */         return defaultAt != null ? defaultAt : inv.getMatchingAttendee(acct);
/*      */       }
/*      */       
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 3100 */       if ((defaultAt != null) && ((!inv.hasRecurId()) || (inv.isLocalOnly()))) {
/* 3101 */         return defaultAt;
/*      */       }
/* 3103 */       return inv.getMatchingAttendee(acct);
/*      */     }
/*      */     
/*      */     List<CalendarItem.ReplyInfo> getAllReplies()
/*      */     {
/* 3108 */       List<CalendarItem.ReplyInfo> toRet = new ArrayList();
/* 3109 */       toRet.addAll(this.mReplies);
/* 3110 */       return toRet;
/*      */     }
/*      */     
/*      */     List<CalendarItem.ReplyInfo> getReplyInfo(Invite inv, String recurIdZ) {
/* 3114 */       assert (inv != null);
/*      */       
/* 3116 */       Map<String, CalendarItem.ReplyInfo> repliesByAddr = new HashMap();
/*      */       
/* 3118 */       if ((recurIdZ == null) && (inv.hasRecurId())) {
/* 3119 */         recurIdZ = inv.getRecurId().getDtZ();
/*      */       }
/* 3121 */       for (CalendarItem.ReplyInfo reply : this.mReplies) {
/* 3122 */         CalendarItem.ReplyInfo toAdd = null;
/* 3123 */         String replyRidZ = null;
/* 3124 */         if (reply.getRecurId() != null)
/*      */         {
/*      */ 
/*      */ 
/* 3128 */           ParsedDateTime dt = reply.getRecurId().getDt();
/* 3129 */           ParsedDateTime dtTmp = (ParsedDateTime)dt.clone();
/* 3130 */           if (inv.isAllDayEvent()) {
/* 3131 */             dtTmp.setHasTime(false);
/*      */           } else {
/* 3133 */             dtTmp.setHasTime(true);
/* 3134 */             dtTmp.toUTC();
/*      */           }
/* 3136 */           replyRidZ = dtTmp.getDateTimePartString(false);
/*      */         }
/* 3138 */         if (inv.hasRecurId())
/*      */         {
/* 3140 */           if ((CalendarItem.ReplyInfo.access$100(reply) != null) && (recurIdZ.equals(replyRidZ)) && (inv.getSeqNo() <= CalendarItem.ReplyInfo.access$700(reply)))
/*      */           {
/* 3142 */             toAdd = reply;
/*      */           }
/*      */           
/*      */ 
/*      */         }
/* 3147 */         else if (recurIdZ == null) {
/* 3148 */           if ((CalendarItem.ReplyInfo.access$100(reply) == null) && (inv.getSeqNo() <= CalendarItem.ReplyInfo.access$700(reply)))
/*      */           {
/* 3150 */             toAdd = reply;
/*      */           }
/*      */         }
/* 3153 */         else if (((CalendarItem.ReplyInfo.access$100(reply) == null) || (recurIdZ.equals(replyRidZ))) && (inv.getSeqNo() <= CalendarItem.ReplyInfo.access$700(reply)))
/*      */         {
/* 3155 */           toAdd = reply;
/*      */         }
/*      */         
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 3163 */         if ((toAdd != null) && (toAdd.getAttendee() != null)) {
/* 3164 */           String addr = toAdd.getAttendee().getAddress();
/* 3165 */           if (addr != null) {
/* 3166 */             CalendarItem.ReplyInfo existing = (CalendarItem.ReplyInfo)repliesByAddr.get(addr);
/* 3167 */             if (existing == null) {
/* 3168 */               repliesByAddr.put(addr, toAdd);
/*      */             }
/* 3170 */             else if (existing.getRecurId() == null) {
/* 3171 */               if (replyRidZ != null)
/*      */               {
/* 3173 */                 repliesByAddr.put(addr, toAdd);
/* 3174 */               } else if (existing.getDtStamp() <= toAdd.getDtStamp())
/*      */               {
/* 3176 */                 repliesByAddr.put(addr, toAdd);
/*      */               }
/*      */             }
/* 3179 */             else if (replyRidZ != null)
/*      */             {
/* 3181 */               if (existing.getDtStamp() <= toAdd.getDtStamp())
/*      */               {
/* 3183 */                 repliesByAddr.put(addr, toAdd);
/*      */               }
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 3191 */       return new ArrayList(repliesByAddr.values());
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public List<ReplyInfo> getAllReplies()
/*      */   {
/* 3202 */     return this.mReplyList.getAllReplies();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public List<ReplyInfo> getReplyInfo(Invite inv, String recurIdZ)
/*      */   {
/* 3215 */     return this.mReplyList.getReplyInfo(inv, recurIdZ);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public void setReplies(List<ReplyInfo> replies)
/*      */     throws ServiceException
/*      */   {
/* 3223 */     this.mReplyList = new ReplyList(replies);
/* 3224 */     saveMetadata();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public String getEffectivePartStat(Invite inv, Instance inst)
/*      */     throws ServiceException
/*      */   {
/* 3236 */     if (!inv.isOrganizer()) {
/* 3237 */       ZAttendee at = this.mReplyList.getEffectiveAttendee(getMailbox().getAccount(), inv, inst);
/* 3238 */       if (at != null) {
/* 3239 */         if (at.hasPartStat()) {
/* 3240 */           return at.getPartStat();
/*      */         }
/* 3242 */         return "NE";
/*      */       }
/*      */     }
/* 3245 */     return inv.getPartStat();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   void modifyPartStat(Account acctOrNull, RecurId recurId, String cnStr, String addressStr, String cutypeStr, String roleStr, String partStatStr, Boolean needsReply, int seqNo, long dtStamp)
/*      */     throws ServiceException
/*      */   {
/* 3268 */     this.mReplyList.modifyPartStat(acctOrNull, recurId, cnStr, addressStr, cutypeStr, roleStr, partStatStr, needsReply, seqNo, dtStamp);
/* 3269 */     if (addressStr != null) {
/* 3270 */       Invite inv = getInvite(recurId);
/* 3271 */       if (inv != null) { ZAttendee at;
/*      */         ZAttendee at;
/* 3273 */         if (acctOrNull != null) {
/* 3274 */           at = inv.getMatchingAttendee(acctOrNull);
/*      */         } else
/* 3276 */           at = inv.getMatchingAttendee(addressStr);
/* 3277 */         if (at != null)
/* 3278 */           at.setPartStat(partStatStr);
/*      */       }
/*      */     }
/* 3281 */     saveMetadata();
/*      */   }
/*      */   
/*      */   boolean processNewInviteReply(Invite reply, String sender) throws ServiceException
/*      */   {
/* 3286 */     List<ZAttendee> attendees = reply.getAttendees();
/*      */     
/* 3288 */     String senderAddress = null;
/* 3289 */     if ((sender != null) && (!sender.isEmpty())) {
/*      */       try {
/* 3291 */         JavaMailInternetAddress address = new JavaMailInternetAddress(sender);
/* 3292 */         senderAddress = address.getAddress();
/*      */       }
/*      */       catch (AddressException e) {}
/*      */     }
/*      */     
/*      */ 
/* 3298 */     if ((senderAddress != null) && (!attendees.isEmpty())) {
/* 3299 */       AccountUtil.AccountAddressMatcher acctMatcher = null;
/* 3300 */       Account acct = Provisioning.getInstance().get(Key.AccountBy.name, senderAddress);
/* 3301 */       if (acct != null) {
/* 3302 */         acctMatcher = new AccountUtil.AccountAddressMatcher(acct);
/*      */       }
/* 3304 */       Iterator<ZAttendee> iter = attendees.iterator();
/* 3305 */       while (iter.hasNext()) {
/* 3306 */         ZAttendee att = (ZAttendee)iter.next();
/*      */         
/* 3308 */         if ((!att.addressMatches(senderAddress)) && ((acctMatcher == null) || (!acctMatcher.matches(att.getAddress())))) {
/* 3309 */           iter.remove();
/*      */         }
/*      */       }
/*      */     }
/*      */     
/*      */ 
/* 3315 */     ZAttendee att1 = !attendees.isEmpty() ? (ZAttendee)attendees.get(0) : null;
/* 3316 */     if (att1 != null) {
/* 3317 */       String ptst = IcalXmlStrMap.sPartStatMap.toIcal(att1.getPartStat());
/* 3318 */       if (!reply.hasRecurId()) {
/* 3319 */         ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s", new Object[] { att1.getAddress(), ptst, Integer.valueOf(this.mId), Integer.valueOf(getFolderId()), reply.isPublic() ? reply.getName() : "(private)", this.mUid });
/*      */ 
/*      */       }
/*      */       else
/*      */       {
/* 3324 */         ZimbraLog.calendar.info("Processing CalendarItem reply: attendee=%s, partstat=%s, id=%d, folderId=%d, subject=\"%s\", UID=%s, recurId=%s", new Object[] { att1.getAddress(), ptst, Integer.valueOf(this.mId), Integer.valueOf(getFolderId()), reply.isPublic() ? reply.getName() : "(private)", this.mUid, reply.getRecurId().getDtZ() });
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/* 3331 */     boolean requirePrivateCheck = requirePrivateCheck(reply);
/* 3332 */     OperationContext octxt = getMailbox().getOperationContext();
/* 3333 */     Account authAccount = octxt != null ? octxt.getAuthenticatedUser() : null;
/* 3334 */     boolean asAdmin = octxt != null ? octxt.isUsingAdminPrivileges() : false;
/* 3335 */     if (!canAccess((short)16, authAccount, asAdmin, requirePrivateCheck)) {
/* 3336 */       throw ServiceException.PERM_DENIED("you do not have sufficient permissions to change this appointment/task's state");
/*      */     }
/* 3338 */     boolean dirty = false;
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 3344 */     Invite matchingInvite = matchingInvite(reply.getRecurId());
/* 3345 */     if (matchingInvite != null)
/*      */     {
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 3365 */       if (((matchingInvite.isOrganizer()) && (matchingInvite.getLastFullSeqNo() > reply.getSeqNo())) || ((!matchingInvite.isOrganizer()) && (matchingInvite.getSeqNo() > reply.getSeqNo())))
/*      */       {
/* 3367 */         sLog.info("Invite-Reply %s is outdated (Calendar entry has higher SEQUENCE), ignoring!", new Object[] { reply });
/* 3368 */         return false;
/*      */       }
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/* 3378 */     for (ZAttendee at : attendees) {
/* 3379 */       if (this.mReplyList.maybeStoreNewReply(reply, at, this)) {
/* 3380 */         dirty = true;
/*      */       }
/*      */     }
/* 3383 */     if (!dirty) {
/* 3384 */       sLog.info("Invite-Reply %s is outdated ignoring!", new Object[] { reply });
/* 3385 */       return false;
/*      */     }
/* 3387 */     if (matchingInvite != null) {
/* 3388 */       matchingInvite.updateMatchingAttendeesFromReply(reply);
/* 3389 */       updateLocalExceptionsWhichMatchSeriesReply(reply);
/*      */     } else {
/* 3391 */       createPseudoExceptionForSingleInstanceReplyIfNecessary(reply);
/*      */     }
/* 3393 */     saveMetadata();
/* 3394 */     return true;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Invite matchingInvite(RecurId aRecurId)
/*      */   {
/* 3403 */     return Invite.matchingInvite(getInvites(), aRecurId);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private void updateLocalExceptionsWhichMatchSeriesReply(Invite reply)
/*      */     throws ServiceException
/*      */   {
/* 3413 */     if ((reply == null) || (reply.getRecurId() != null)) {
/* 3414 */       return;
/*      */     }
/* 3416 */     Recurrence.IRecurrence replyRecurrence = reply.getRecurrence();
/* 3417 */     if (replyRecurrence == null) {
/* 3418 */       sLog.debug("Giving up on trying to match series reply to local exceptions - no recurrence in reply");
/* 3419 */       return;
/*      */     }
/* 3421 */     for (int i = 0; i < numInvites(); i++) {
/* 3422 */       Invite cur = getInvite(i);
/* 3423 */       if ((cur.classPropSetByMe()) && (cur.getRecurId() != null))
/*      */       {
/*      */ 
/* 3426 */         ParsedDateTime recurIdDT = cur.getRecurId().getDt();
/* 3427 */         ParsedDateTime startDT = cur.getStartTime();
/*      */         
/* 3429 */         if ((recurIdDT != null) && (startDT != null) && (recurIdDT.sameTime(startDT)))
/*      */         {
/*      */ 
/* 3432 */           long utcTime = recurIdDT.getUtcTime();
/*      */           
/*      */ 
/* 3435 */           List<Instance> instances = Recurrence.expandInstances(replyRecurrence, getId(), utcTime - 2000L, utcTime + 2000L);
/*      */           
/* 3437 */           if ((instances != null) && (instances.size() == 1))
/*      */           {
/*      */ 
/* 3440 */             cur.updateMatchingAttendeesFromReply(reply);
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   private void createPseudoExceptionForSingleInstanceReplyIfNecessary(Invite reply)
/*      */     throws ServiceException
/*      */   {
/* 3451 */     if ((reply == null) || (reply.getRecurId() == null)) {
/* 3452 */       return;
/*      */     }
/* 3454 */     Recurrence.RecurrenceRule recurrenceRule = null;
/* 3455 */     if ((this.mRecurrence == null) || (!(this.mRecurrence instanceof Recurrence.RecurrenceRule))) {
/* 3456 */       return;
/*      */     }
/* 3458 */     recurrenceRule = (Recurrence.RecurrenceRule)this.mRecurrence;
/* 3459 */     Collection<Instance> instancesNear = instancesNear(reply.getRecurId());
/* 3460 */     if (!instancesNear.isEmpty())
/*      */     {
/* 3462 */       for (int i = 0; i < numInvites(); i++) {
/* 3463 */         Invite cur = getInvite(i);
/* 3464 */         if (cur.getRecurId() == null) {
/*      */           try {
/* 3466 */             ParsedDateTime pdt = ParsedDateTime.parseUtcOnly(reply.getRecurId().getDtZ());
/* 3467 */             Invite localException = cur.makeInstanceInvite(pdt);
/* 3468 */             localException.setDtStamp(System.currentTimeMillis());
/* 3469 */             localException.updateMatchingAttendeesFromReply(reply);
/* 3470 */             localException.setClassPropSetByMe(true);
/* 3471 */             this.mInvites.add(localException);
/*      */             
/* 3473 */             recurrenceRule.addException(new Recurrence.ExceptionRule(reply.getRecurId(), localException.getStartTime(), localException.getEffectiveDuration(), new InviteInfo(localException)));
/*      */           }
/*      */           catch (ParseException e)
/*      */           {
/* 3477 */             sLog.debug("Unexpected exception - not updating calendar invite with pseudo exception", e);
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   public InputStream getRawMessage() throws ServiceException
/*      */   {
/* 3486 */     return getContentStream();
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   void appendRawCalendarData(ZCalendar.ZVCalendar cal, boolean useOutlookCompatMode, boolean ignoreErrors, boolean allowPrivateAccess)
/*      */     throws ServiceException
/*      */   {
/* 3494 */     Invite[] invs = getInvites();
/* 3495 */     for (Invite inv : invs) {
/*      */       try {
/* 3497 */         cal.addComponent(inv.newToVComponent(useOutlookCompatMode, allowPrivateAccess));
/*      */       } catch (ServiceException e) {
/* 3499 */         if (ignoreErrors) {
/* 3500 */           ZimbraLog.calendar.warn("Error retrieving iCalendar data for item " + inv.getMailItemId() + ": " + e.getMessage(), e);
/*      */         }
/*      */         else
/*      */         {
/* 3504 */           throw e; }
/*      */       }
/*      */     }
/*      */   }
/*      */   
/*      */   public MimeMessage getMimeMessage() throws ServiceException {
/* 3510 */     return MessageCache.getMimeMessage(this, !DebugConfig.disableMimeConvertersForCalendarBlobs);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   public MimeMessage getSubpartMessage(int subId)
/*      */     throws ServiceException
/*      */   {
/*      */     try
/*      */     {
/* 3520 */       MimeBodyPart mbp = findBodyBySubId(subId);
/* 3521 */       return mbp == null ? null : Mime.getMessageContent(mbp);
/*      */     } catch (IOException e) {
/* 3523 */       throw ServiceException.FAILURE("IOException while getting MimeMessage for item " + this.mId, e);
/*      */     } catch (MessagingException e) {
/* 3525 */       throw ServiceException.FAILURE("MessagingException while getting MimeMessage for item " + this.mId, e);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public Pair<MimeMessage, Integer> getSubpartMessageData(int subId)
/*      */     throws ServiceException
/*      */   {
/*      */     try
/*      */     {
/* 3538 */       MimeBodyPart mbp = findBodyBySubId(subId);
/* 3539 */       if (mbp == null)
/* 3540 */         return null;
/* 3541 */       return new Pair(Mime.getMessageContent(mbp), Integer.valueOf(mbp.getSize()));
/*      */     } catch (IOException e) {
/* 3543 */       throw ServiceException.FAILURE("IOException while getting MimeMessage for item " + this.mId, e);
/*      */     } catch (MessagingException e) {
/* 3545 */       throw ServiceException.FAILURE("MessagingException while getting MimeMessage for item " + this.mId, e);
/*      */     }
/*      */   }
/*      */   
/*      */   private MimeBodyPart findBodyBySubId(int subId) throws ServiceException {
/* 3550 */     if (getSize() <= 0L)
/* 3551 */       return null;
/* 3552 */     MimeMessage mm = null;
/*      */     try {
/* 3554 */       mm = MessageCache.getMimeMessage(this, !DebugConfig.disableMimeConvertersForCalendarBlobs);
/*      */       
/*      */ 
/* 3557 */       Object obj = null;
/*      */       MimeMultipart mmp;
/* 3559 */       try { obj = mm.getContent();
/* 3560 */         MimeMultipart mmp; if ((obj instanceof MimeMultipart)) {
/* 3561 */           mmp = (MimeMultipart)obj;
/*      */         } else {
/* 3563 */           throw ServiceException.FAILURE("Expected MimeMultipart, but got " + obj.getClass().getName() + ": id=" + this.mId + ", content=" + obj.toString(), null);
/*      */         }
/*      */       }
/*      */       finally
/*      */       {
/* 3568 */         if ((obj instanceof InputStream)) {
/* 3569 */           ByteUtil.closeStream((InputStream)obj);
/*      */         }
/*      */       }
/*      */       
/*      */ 
/* 3574 */       int numParts = mmp.getCount();
/*      */       
/* 3576 */       for (int i = 0; i < numParts; i++) {
/* 3577 */         MimeBodyPart mbp = (MimeBodyPart)mmp.getBodyPart(i);
/* 3578 */         String[] hdrs = mbp.getHeader("invId");
/*      */         
/* 3580 */         if ((hdrs != null) && (hdrs.length > 0) && (Integer.parseInt(hdrs[0]) == subId))
/* 3581 */           return mbp;
/*      */       }
/* 3583 */       return null;
/*      */     } catch (IOException e) {
/* 3585 */       throw ServiceException.FAILURE("IOException while getting MimeMessage for item " + this.mId, e);
/*      */     } catch (MessagingException e) {
/* 3587 */       throw ServiceException.FAILURE("MessagingException while getting MimeMessage for item " + this.mId, e);
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   protected abstract String processPartStat(Invite paramInvite, MimeMessage paramMimeMessage, boolean paramBoolean, String paramString)
/*      */     throws ServiceException;
/*      */   
/*      */ 
/*      */ 
/*      */   public static class AlarmData
/*      */   {
/*      */     private static final long NO_SNOOZE = Long.MAX_VALUE;
/*      */     
/* 3602 */     private long mNextAt = Long.MAX_VALUE;
/* 3603 */     private long mSnoozeUntil = Long.MAX_VALUE;
/*      */     private final long mNextInstStart;
/*      */     private final int mInvId;
/*      */     private final int mCompNum;
/*      */     private final Alarm mAlarm;
/*      */     private static final String FNAME_NEXT_AT = "na";
/*      */     
/*      */     public AlarmData(long next, long snoozeUntil, long nextInstStart, int invId, int compNum, Alarm alarm)
/*      */     {
/* 3612 */       this.mNextAt = next;
/* 3613 */       this.mSnoozeUntil = snoozeUntil;
/* 3614 */       this.mNextInstStart = nextInstStart;
/* 3615 */       this.mInvId = invId;
/* 3616 */       this.mCompNum = compNum;
/* 3617 */       this.mAlarm = alarm;
/*      */     }
/*      */     
/*      */     public Object clone()
/*      */     {
/* 3622 */       return new AlarmData(this.mNextAt, this.mSnoozeUntil, this.mNextInstStart, this.mInvId, this.mCompNum, this.mAlarm.newCopy());
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/*      */     public long getNextAt()
/*      */     {
/* 3630 */       return this.mSnoozeUntil != Long.MAX_VALUE ? this.mSnoozeUntil : this.mNextAt;
/*      */     }
/*      */     
/* 3633 */     private long getNextAtBase() { return this.mNextAt; }
/* 3634 */     public long getSnoozeUntil() { return this.mSnoozeUntil; }
/* 3635 */     private void setSnoozeUntil(long s) { this.mSnoozeUntil = s; }
/* 3636 */     public long getNextInstanceStart() { return this.mNextInstStart; }
/* 3637 */     public int getInvId() { return this.mInvId; }
/* 3638 */     public int getCompNum() { return this.mCompNum; }
/* 3639 */     public Alarm getAlarm() { return this.mAlarm; }
/*      */     
/*      */     private static final String FNAME_SNOOZE = "snuz";
/*      */     private static final String FNAME_NEXT_INSTANCE_START = "nis";
/*      */     private static final String FNAME_INV_ID = "invId";
/*      */     private static final String FNAME_COMP_NUM = "compNum";
/*      */     private static final String FNAME_ALARM = "alarm";
/*      */     static AlarmData decodeMetadata(Metadata meta)
/*      */       throws ServiceException
/*      */     {
/* 3649 */       long nextAt = meta.getLong("na");
/* 3650 */       long snoozeUntil = meta.getLong("snuz", Long.MAX_VALUE);
/* 3651 */       long nextInstStart = meta.getLong("nis");
/* 3652 */       int invId = (int)meta.getLong("invId");
/* 3653 */       int compNum = (int)meta.getLong("compNum");
/* 3654 */       Alarm alarm = null;
/* 3655 */       Metadata metaAlarm = meta.getMap("alarm", true);
/* 3656 */       if (metaAlarm != null)
/* 3657 */         alarm = Alarm.decodeMetadata(metaAlarm);
/* 3658 */       return new AlarmData(nextAt, snoozeUntil, nextInstStart, invId, compNum, alarm);
/*      */     }
/*      */     
/*      */     Metadata encodeMetadata() {
/* 3662 */       Metadata meta = new Metadata();
/* 3663 */       meta.put("na", this.mNextAt);
/* 3664 */       if (this.mSnoozeUntil != Long.MAX_VALUE)
/* 3665 */         meta.put("na", this.mSnoozeUntil);
/* 3666 */       meta.put("nis", this.mNextInstStart);
/* 3667 */       meta.put("invId", this.mInvId);
/* 3668 */       meta.put("compNum", this.mCompNum);
/* 3669 */       if (this.mAlarm != null)
/* 3670 */         meta.put("alarm", this.mAlarm.encodeMetadata());
/* 3671 */       return meta;
/*      */     }
/*      */   }
/*      */   
/*      */   public boolean hasAlarm() {
/* 3676 */     if ((this.mInvites != null) && (this.mInvites.size() > 0)) {
/* 3677 */       for (Invite inv : this.mInvites) {
/* 3678 */         if (inv.hasAlarm())
/* 3679 */           return true;
/*      */       }
/*      */     }
/* 3682 */     return false;
/*      */   }
/*      */   
/*      */   void updateNextAlarm(long nextAlarm, boolean dismissed) throws ServiceException {
/* 3686 */     updateNextAlarm(nextAlarm, this.mAlarmData, dismissed);
/*      */   }
/*      */   
/*      */   void updateNextAlarm(long nextAlarm, AlarmData savedAlarmData, boolean dismissed) throws ServiceException {
/* 3690 */     boolean hadAlarm = this.mAlarmData != null;
/* 3691 */     this.mAlarmData = savedAlarmData;
/* 3692 */     recomputeNextAlarm(nextAlarm, true, dismissed);
/* 3693 */     if (this.mAlarmData != null) {
/* 3694 */       long newNextAlarm = this.mAlarmData.getNextAtBase();
/* 3695 */       if ((newNextAlarm > 0L) && (newNextAlarm < this.mStartTime))
/* 3696 */         this.mStartTime = newNextAlarm;
/* 3697 */       if (ZimbraLog.calendar.isDebugEnabled()) {
/* 3698 */         ZimbraLog.calendar.debug("Setting next alarm for %s to %d.", new Object[] { getMailopContext(this), Long.valueOf(nextAlarm) });
/*      */       }
/*      */       
/* 3701 */       DbMailItem.updateInCalendarItemTable(this);
/*      */     }
/* 3703 */     if ((this.mAlarmData != null) || (hadAlarm)) {
/* 3704 */       saveMetadata();
/*      */     }
/*      */   }
/*      */   
/*      */ 
/*      */   private long getNextAlarmRecurrenceExpansionLimit()
/*      */   {
/* 3711 */     long fromTime = Math.max(getStartTime(), System.currentTimeMillis());
/* 3712 */     long endTime = fromTime + 31536000000L;
/* 3713 */     long itemEndTime = getEndTime();
/* 3714 */     if ((itemEndTime < endTime) && (itemEndTime >= getStartTime()))
/* 3715 */       endTime = itemEndTime;
/* 3716 */     return endTime;
/*      */   }
/*      */   
/*      */   private void recomputeNextAlarm(long nextAlarm, boolean skipAlarmDefChangeCheck, boolean dismissed) throws ServiceException
/*      */   {
/* 3721 */     this.mAlarmData = getNextAlarm(nextAlarm, skipAlarmDefChangeCheck, this.mAlarmData, dismissed, false);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public AlarmData getNextEmailAlarm()
/*      */     throws ServiceException
/*      */   {
/* 3731 */     return getNextAlarm(System.currentTimeMillis(), true, null, false, true);
/*      */   }
/*      */   
/*      */   private AlarmData getNextAlarm(long nextAlarm, boolean skipAlarmDefChangeCheck, AlarmData currentNextAlarmData, boolean dismissed, boolean forEmailAction)
/*      */     throws ServiceException
/*      */   {
/* 3737 */     if ((nextAlarm == -1L) || (!hasAlarm())) {
/* 3738 */       return null;
/*      */     }
/*      */     
/* 3741 */     long now = getMailbox().getOperationTimestampMillis();
/*      */     long atOrAfter;
/*      */     long snoozeUntil;
/* 3744 */     long atOrAfter; if (nextAlarm == 0L) {
/*      */       long snoozeUntil;
/* 3746 */       if (currentNextAlarmData != null) {
/* 3747 */         long atOrAfter = currentNextAlarmData.getNextAtBase();
/* 3748 */         snoozeUntil = currentNextAlarmData.getSnoozeUntil();
/*      */       } else { long snoozeUntil;
/* 3750 */         atOrAfter = snoozeUntil = now;
/*      */       } } else { long atOrAfter;
/* 3752 */       if (nextAlarm == -2L) {
/*      */         long snoozeUntil;
/* 3754 */         atOrAfter = snoozeUntil = now; } else { long snoozeUntil;
/* 3755 */         if ((!dismissed) && (currentNextAlarmData != null))
/*      */         {
/* 3757 */           long atOrAfter = currentNextAlarmData.getNextAtBase();
/* 3758 */           snoozeUntil = nextAlarm;
/*      */         }
/*      */         else {
/* 3761 */           atOrAfter = snoozeUntil = nextAlarm;
/*      */         } } }
/* 3763 */     if (atOrAfter <= 0L) {
/* 3764 */       atOrAfter = snoozeUntil = now;
/*      */     }
/* 3766 */     if ((snoozeUntil != Long.MAX_VALUE) && (snoozeUntil < atOrAfter)) {
/* 3767 */       snoozeUntil = atOrAfter;
/*      */     }
/*      */     
/*      */ 
/*      */ 
/*      */ 
/* 3773 */     long startTime = atOrAfter;
/* 3774 */     if (startTime > now)
/*      */     {
/*      */ 
/* 3777 */       startTime = now;
/*      */     }
/* 3779 */     long endTime = getNextAlarmRecurrenceExpansionLimit();
/* 3780 */     Collection<Instance> instances = expandInstances(startTime, endTime, false);
/*      */     
/*      */ 
/* 3783 */     if ((atOrAfter > 0L) && (!skipAlarmDefChangeCheck))
/*      */     {
/*      */ 
/* 3786 */       boolean alarmDefChanged = true;
/* 3787 */       long savedNextInstStart = currentNextAlarmData != null ? currentNextAlarmData.getNextInstanceStart() : 0L;
/* 3788 */       for (Instance inst : instances) {
/* 3789 */         long instStart = inst.getStart();
/* 3790 */         long instEnd = inst.getEnd();
/* 3791 */         if ((inst.hasStart()) && (inst.hasEnd()))
/*      */         {
/* 3793 */           if ((instEnd > startTime) && (
/*      */           
/*      */ 
/* 3796 */             (instStart >= startTime) || (!(this instanceof Appointment))))
/*      */           {
/*      */ 
/*      */ 
/*      */ 
/* 3801 */             if (instStart > savedNextInstStart)
/*      */               break; }
/*      */         } else {
/* 3804 */           InviteInfo invId = inst.getInviteInfo();
/* 3805 */           Invite inv = getInvite(invId.getMsgId(), invId.getComponentId());
/* 3806 */           Iterator<Alarm> alarmsIter = inv.alarmsIterator();
/* 3807 */           while (alarmsIter.hasNext()) {
/* 3808 */             Alarm alarm = (Alarm)alarmsIter.next();
/* 3809 */             long currTrigger = alarm.getTriggerTime(instStart, instEnd);
/* 3810 */             if (currTrigger == atOrAfter)
/*      */             {
/*      */ 
/* 3813 */               alarmDefChanged = false;
/* 3814 */               break;
/*      */             }
/*      */           }
/*      */         }
/*      */       }
/* 3819 */       if (alarmDefChanged)
/*      */       {
/*      */ 
/*      */ 
/* 3823 */         atOrAfter = snoozeUntil = now;
/*      */       }
/*      */     }
/*      */     
/* 3827 */     AlarmData alarmData = getNextAlarmHelper(atOrAfter, snoozeUntil, instances, startTime, forEmailAction);
/* 3828 */     if ((alarmData == null) && ((this instanceof Task)))
/*      */     {
/* 3830 */       return getNextAlarmHelperForTasks(atOrAfter, snoozeUntil, forEmailAction);
/*      */     }
/* 3832 */     return alarmData;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */   private AlarmData getNextAlarmHelper(long atOrAfter, long snoozeUntil, Collection<Instance> instances, long rangeStart, boolean forEmailAction)
/*      */   {
/* 3839 */     Alarm alarm1 = null;Alarm alarm2 = null;
/* 3840 */     long trigger1 = Long.MIN_VALUE;long trigger2 = Long.MAX_VALUE;
/* 3841 */     long instStart1 = 0L;long instStart2 = 0L;
/* 3842 */     int invId1 = 0;int compNum1 = 0;int invId2 = 0;int compNum2 = 0;
/* 3843 */     for (Instance inst : instances) {
/* 3844 */       long instStart = inst.getStart();
/* 3845 */       long instEnd = inst.getEnd();
/* 3846 */       if ((!inst.hasStart()) || (!inst.hasEnd()) || (
/*      */       
/* 3848 */         (instEnd > rangeStart) && (
/*      */         
/*      */ 
/* 3851 */         (instStart >= rangeStart) || (!(this instanceof Appointment)))))
/*      */       {
/*      */ 
/* 3854 */         InviteInfo invId = inst.getInviteInfo();
/* 3855 */         Invite inv = getInvite(invId.getMsgId(), invId.getComponentId());
/* 3856 */         assert (inv != null);
/*      */         
/* 3858 */         for (Iterator<Alarm> alarms = inv.alarmsIterator(); alarms.hasNext();) {
/* 3859 */           Alarm alarm = (Alarm)alarms.next();
/* 3860 */           if (Alarm.Action.EMAIL.equals(alarm.getAction()) == forEmailAction) {
/* 3861 */             long trg = alarm.getTriggerTime(instStart, instEnd);
/* 3862 */             if (trg <= atOrAfter) {
/* 3863 */               if (trg > trigger1) {
/* 3864 */                 trigger1 = trg;alarm1 = alarm;instStart1 = instStart;
/* 3865 */                 invId1 = invId.getMsgId();compNum1 = invId.getComponentId();
/*      */               }
/*      */             }
/* 3868 */             else if (trg < trigger2) {
/* 3869 */               trigger2 = trg;alarm2 = alarm;instStart2 = instStart;
/* 3870 */               invId2 = invId.getMsgId();compNum2 = invId.getComponentId();
/*      */             }
/*      */           }
/*      */         }
/*      */         
/* 3875 */         if ((alarm1 != null) && (alarm2 != null)) {
/*      */           break;
/*      */         }
/*      */       }
/*      */     }
/* 3880 */     AlarmData ad1 = alarm1 != null ? new AlarmData(trigger1, snoozeUntil, instStart1, invId1, compNum1, alarm1) : null;
/* 3881 */     AlarmData ad2 = alarm2 != null ? new AlarmData(trigger2, Long.MAX_VALUE, instStart2, invId2, compNum2, alarm2) : null;
/* 3882 */     return chooseNextAlarm(atOrAfter, snoozeUntil, ad1, ad2);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private AlarmData getNextAlarmHelperForTasks(long atOrAfter, long snoozeUntil, boolean forEmailAction)
/*      */   {
/* 3898 */     Alarm alarm1 = null;Alarm alarm2 = null;
/* 3899 */     long trigger1 = Long.MIN_VALUE;long trigger2 = Long.MAX_VALUE;
/* 3900 */     long instStart1 = 0L;long instStart2 = 0L;
/* 3901 */     int invId1 = 0;int compNum1 = 0;int invId2 = 0;int compNum2 = 0;
/* 3902 */     for (Iterator i$ = this.mInvites.iterator(); i$.hasNext();) { inv = (Invite)i$.next();
/* 3903 */       if (!inv.isCancel())
/*      */       {
/*      */ 
/* 3906 */         for (alarms = inv.alarmsIterator(); alarms.hasNext();) {
/* 3907 */           Alarm alarm = (Alarm)alarms.next();
/* 3908 */           if ((Alarm.Action.EMAIL.equals(alarm.getAction()) == forEmailAction) && (alarm.getTriggerAbsolute() != null)) {
/* 3909 */             long trg = alarm.getTriggerAbsolute().getUtcTime();
/* 3910 */             if (trg <= atOrAfter) {
/* 3911 */               if (trg > trigger1) {
/* 3912 */                 trigger1 = trg;alarm1 = alarm;
/* 3913 */                 instStart1 = inv.getStartTime() != null ? inv.getStartTime().getUtcTime() : 0L;
/* 3914 */                 invId1 = inv.getMailItemId();compNum1 = inv.getComponentNum();
/*      */               }
/*      */             }
/* 3917 */             else if (trg < trigger2) {
/* 3918 */               trigger2 = trg;alarm2 = alarm;
/* 3919 */               instStart2 = inv.getStartTime() != null ? inv.getStartTime().getUtcTime() : 0L;
/* 3920 */               invId2 = inv.getMailItemId();compNum2 = inv.getComponentNum();
/*      */             }
/*      */           }
/*      */         } }
/*      */     }
/*      */     Invite inv;
/*      */     Iterator<Alarm> alarms;
/* 3927 */     AlarmData ad1 = alarm1 != null ? new AlarmData(trigger1, snoozeUntil, instStart1, invId1, compNum1, alarm1) : null;
/* 3928 */     AlarmData ad2 = alarm2 != null ? new AlarmData(trigger2, Long.MAX_VALUE, instStart2, invId2, compNum2, alarm2) : null;
/* 3929 */     return chooseNextAlarm(atOrAfter, snoozeUntil, ad1, ad2);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static final long MILLIS_IN_YEAR = 31536000000L;
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static final long MILLIS_IN_DAY = 86400000L;
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   private AlarmData chooseNextAlarm(long atOrAfter, long snoozeUntil, AlarmData alarmData1, AlarmData alarmData2)
/*      */   {
/* 3981 */     if (snoozeUntil <= atOrAfter) {
/* 3982 */       snoozeUntil = Long.MAX_VALUE;
/*      */     }
/* 3984 */     if (alarmData1 != null) {
/* 3985 */       if (alarmData2 != null) {
/* 3986 */         if (snoozeUntil == Long.MAX_VALUE) {
/* 3987 */           if (alarmData1.getNextAtBase() == atOrAfter)
/*      */           {
/* 3989 */             alarmData1.setSnoozeUntil(Long.MAX_VALUE);
/* 3990 */             return alarmData1;
/*      */           }
/*      */           
/* 3993 */           alarmData2.setSnoozeUntil(Long.MAX_VALUE);
/* 3994 */           return alarmData2;
/*      */         }
/*      */         
/* 3997 */         if ((alarmData1.getNextAtBase() == atOrAfter) && (snoozeUntil < alarmData2.getNextAtBase()))
/*      */         {
/* 3999 */           alarmData1.setSnoozeUntil(snoozeUntil);
/* 4000 */           return alarmData1;
/*      */         }
/*      */         
/* 4003 */         alarmData2.setSnoozeUntil(Long.MAX_VALUE);
/* 4004 */         return alarmData2;
/*      */       }
/*      */       
/*      */ 
/*      */ 
/* 4009 */       if (alarmData1.getNextAtBase() == atOrAfter) {
/* 4010 */         alarmData1.setSnoozeUntil(snoozeUntil);
/* 4011 */         return alarmData1;
/*      */       }
/* 4013 */       return null;
/*      */     }
/*      */     
/*      */ 
/* 4017 */     if (alarmData2 != null) {
/* 4018 */       alarmData2.setSnoozeUntil(Long.MAX_VALUE);
/* 4019 */       return alarmData2;
/*      */     }
/* 4021 */     return null;
/*      */   }
/*      */   
/*      */   public static class NextAlarms
/*      */   {
/*      */     private final Map<Integer, Long> mTriggerMap;
/*      */     private final Map<Integer, Long> mInstStartMap;
/*      */     
/*      */     public NextAlarms() {
/* 4030 */       this.mTriggerMap = new HashMap();
/* 4031 */       this.mInstStartMap = new HashMap();
/*      */     }
/*      */     
/* 4034 */     public void add(int pos, long triggerAt, long instStart) { this.mTriggerMap.put(Integer.valueOf(pos), Long.valueOf(triggerAt));
/* 4035 */       this.mInstStartMap.put(Integer.valueOf(pos), Long.valueOf(instStart));
/*      */     }
/*      */     
/* 4038 */     public long getTriggerTime(int pos) { Long triggerAt = (Long)this.mTriggerMap.get(Integer.valueOf(pos));
/* 4039 */       if (triggerAt != null) {
/* 4040 */         return triggerAt.longValue();
/*      */       }
/* 4042 */       return 0L;
/*      */     }
/*      */     
/* 4045 */     public long getInstStart(int pos) { Long start = (Long)this.mInstStartMap.get(Integer.valueOf(pos));
/* 4046 */       if (start != null) {
/* 4047 */         return start.longValue();
/*      */       }
/* 4049 */       return 0L;
/*      */     }
/*      */     
/* 4052 */     public Iterator<Integer> posIterator() { return this.mTriggerMap.keySet().iterator(); }
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public NextAlarms computeNextAlarms(Invite inv, long lastAt)
/*      */     throws ServiceException
/*      */   {
/* 4065 */     int numAlarms = inv.getAlarms().size();
/* 4066 */     Map<Integer, Long> lastAlarmsAt = new HashMap(numAlarms);
/* 4067 */     for (int i = 0; i < numAlarms; i++) {
/* 4068 */       lastAlarmsAt.put(Integer.valueOf(i), Long.valueOf(lastAt));
/*      */     }
/* 4070 */     return computeNextAlarms(inv, lastAlarmsAt);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public NextAlarms computeNextAlarms(Invite inv, Map<Integer, Long> lastAlarmsAt)
/*      */     throws ServiceException
/*      */   {
/* 4084 */     NextAlarms result = new NextAlarms();
/*      */     
/* 4086 */     if (inv.getRecurrence() == null)
/*      */     {
/* 4088 */       long instStart = 0L;long instEnd = 0L;
/* 4089 */       ParsedDateTime dtstart = inv.getStartTime();
/* 4090 */       if (dtstart != null)
/* 4091 */         instStart = dtstart.getUtcTime();
/* 4092 */       ParsedDateTime dtend = inv.getEffectiveEndTime();
/* 4093 */       if (dtend != null)
/* 4094 */         instEnd = dtend.getUtcTime();
/* 4095 */       List<Alarm> alarms = inv.getAlarms();
/* 4096 */       int index = 0;
/* 4097 */       for (Iterator<Alarm> iter = alarms.iterator(); iter.hasNext(); index++) {
/* 4098 */         Alarm alarm = (Alarm)iter.next();
/* 4099 */         Long lastAtLong = (Long)lastAlarmsAt.get(Integer.valueOf(index));
/* 4100 */         if (lastAtLong != null) {
/* 4101 */           long lastAt = lastAtLong.longValue();
/* 4102 */           long triggerAt = alarm.getTriggerTime(instStart, instEnd);
/* 4103 */           if (lastAt < triggerAt) {
/* 4104 */             result.add(index, triggerAt, instStart);
/*      */           }
/*      */         }
/*      */       }
/*      */     }
/*      */     else {
/* 4110 */       long oldest = Long.MAX_VALUE;
/* 4111 */       for (Iterator i$ = lastAlarmsAt.values().iterator(); i$.hasNext();) { long lastAt = ((Long)i$.next()).longValue();
/* 4112 */         oldest = Math.min(oldest, lastAt);
/*      */       }
/* 4114 */       long endTime = getNextAlarmRecurrenceExpansionLimit();
/* 4115 */       Collection<Instance> instances = expandInstances(oldest, endTime, false);
/*      */       
/* 4117 */       List<Alarm> alarms = inv.getAlarms();
/* 4118 */       int index = 0;
/* 4119 */       Alarm alarm; long lastAt; for (Iterator<Alarm> iter = alarms.iterator(); iter.hasNext(); index++) {
/* 4120 */         alarm = (Alarm)iter.next();
/* 4121 */         Long lastAtLong = (Long)lastAlarmsAt.get(Integer.valueOf(index));
/* 4122 */         if (lastAtLong != null) {
/* 4123 */           lastAt = lastAtLong.longValue();
/* 4124 */           for (Instance inst : instances)
/* 4125 */             if (!inst.isException())
/*      */             {
/* 4127 */               long instStart = inst.getStart();
/* 4128 */               if ((instStart >= lastAt) || (!inst.hasStart()))
/*      */               {
/* 4130 */                 long triggerAt = alarm.getTriggerTime(instStart, inst.getEnd());
/* 4131 */                 if (lastAt < triggerAt) {
/* 4132 */                   result.add(index, triggerAt, instStart);
/*      */                   
/* 4134 */                   break;
/*      */                 }
/*      */               }
/*      */             }
/*      */         }
/*      */       } }
/* 4140 */     return result;
/*      */   }
/*      */   
/*      */   boolean move(Folder target) throws ServiceException
/*      */   {
/* 4145 */     Invite defInv = getDefaultInviteOrNull();
/*      */     String sbj;
/* 4147 */     String sbj; if (defInv != null) {
/* 4148 */       sbj = defInv.isPublic() ? defInv.getName() : "(private)";
/*      */     } else
/* 4150 */       sbj = "(none)";
/* 4151 */     ZimbraLog.calendar.info("Moving CalendarItem: id=%d, src=%s, dest=%s, subject=\"%s\", UID=%s", new Object[] { Integer.valueOf(this.mId), getMailopContext(getFolder()), getMailopContext(target), sbj, this.mUid });
/*      */     
/*      */ 
/* 4154 */     if (!isPublic()) {
/* 4155 */       if (!canAccess((short)1024)) {
/* 4156 */         throw ServiceException.PERM_DENIED("you do not have permission to move private calendar item from the current folder");
/*      */       }
/* 4158 */       if ((target.getId() != 3) && (!target.canAccess((short)1024))) {
/* 4159 */         throw ServiceException.PERM_DENIED("you do not have permission to move private calendar item to the target folder");
/*      */       }
/*      */     }
/*      */     
/* 4163 */     addRevision(true);
/* 4164 */     return super.move(target);
/*      */   }
/*      */   
/*      */   void delete(boolean writeTombstones) throws ServiceException
/*      */   {
/* 4169 */     Invite defInv = getDefaultInviteOrNull();
/*      */     String sbj;
/* 4171 */     String sbj; if (defInv != null) {
/* 4172 */       sbj = defInv.isPublic() ? defInv.getName() : "(private)";
/*      */     } else
/* 4174 */       sbj = "(none)";
/* 4175 */     ZimbraLog.calendar.info("Deleting CalendarItem: id=%d, folderId=%d, subject=\"%s\", UID=%s", new Object[] { Integer.valueOf(this.mId), Integer.valueOf(getFolderId()), sbj, this.mUid });
/*      */     
/*      */ 
/* 4178 */     if ((!isPublic()) && (!canAccess((short)1024))) {
/* 4179 */       throw ServiceException.PERM_DENIED("you do not have permission to delete private calendar item from the current folder");
/*      */     }
/* 4181 */     super.delete(writeTombstones);
/*      */   }
/*      */   
/*      */   MailItem copy(Folder folder, int id, String uuid, MailItem parent) throws IOException, ServiceException
/*      */   {
/* 4186 */     if (!isPublic()) {
/* 4187 */       boolean privateAccessSrc = canAccess((short)1024);
/* 4188 */       boolean privateAccessDest = folder.canAccess((short)1024);
/* 4189 */       if (!privateAccessSrc) {
/* 4190 */         throw ServiceException.PERM_DENIED("you do not have permission to copy private calendar item from the current folder");
/*      */       }
/* 4192 */       if (!privateAccessDest) {
/* 4193 */         throw ServiceException.PERM_DENIED("you do not have permission to copy private calendar item to the target folder");
/*      */       }
/*      */     }
/* 4196 */     return super.copy(folder, id, uuid, parent);
/*      */   }
/*      */   
/*      */   void rename(String name, Folder target) throws ServiceException
/*      */   {
/* 4201 */     if (!isPublic()) {
/* 4202 */       boolean privateAccessSrc = canAccess((short)1024);
/* 4203 */       boolean privateAccessDest = target.canAccess((short)1024);
/* 4204 */       if (!privateAccessSrc) {
/* 4205 */         throw ServiceException.PERM_DENIED("you do not have permission to rename/move private calendar item from the current folder");
/*      */       }
/* 4207 */       if (!privateAccessDest) {
/* 4208 */         throw ServiceException.PERM_DENIED("you do not have permission to move private calendar item to the target folder");
/*      */       }
/*      */     }
/* 4211 */     super.rename(name, target);
/*      */   }
/*      */   
/*      */   boolean canAccess(short rightsNeeded, Account authuser, boolean asAdmin) throws ServiceException
/*      */   {
/* 4216 */     return canAccess(rightsNeeded, authuser, asAdmin, false);
/*      */   }
/*      */   
/*      */   private boolean canAccess(short rightsNeeded, Account authuser, boolean asAdmin, boolean requirePrivateCheck) throws ServiceException
/*      */   {
/* 4221 */     if ((requirePrivateCheck) && (!isPublic()))
/*      */     {
/* 4223 */       short writeAccess = 10;
/* 4224 */       if (((rightsNeeded & writeAccess) != 0) && 
/* 4225 */         (!super.canAccess((short)1024, authuser, asAdmin))) {
/* 4226 */         return false;
/*      */       }
/*      */     }
/* 4229 */     return super.canAccess(rightsNeeded, authuser, asAdmin);
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */   private boolean requirePrivateCheck(Invite newInvite)
/*      */   {
/* 4237 */     if (!newInvite.isPublic())
/*      */     {
/* 4239 */       return true;
/*      */     }
/* 4241 */     if (!isPublic()) {
/* 4242 */       RecurId rid = newInvite.getRecurId();
/*      */       
/* 4244 */       if ((rid == null) && (newInvite.isCancel()))
/* 4245 */         return true;
/* 4246 */       Invite current = getInvite(rid);
/*      */       
/* 4248 */       if ((current == null) && (rid != null))
/* 4249 */         current = getInvite((RecurId)null);
/* 4250 */       if ((current != null) && (!current.isPublic()))
/*      */       {
/* 4252 */         return true;
/*      */       }
/*      */       
/* 4255 */       return false;
/*      */     }
/*      */     
/*      */ 
/* 4259 */     return false;
/*      */   }
/*      */   
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */ 
/*      */   public static Map<Integer, MimeMessage> decomposeBlob(byte[] digestBlob)
/*      */     throws ServiceException
/*      */   {
/* 4271 */     Map<Integer, MimeMessage> map = new HashMap();
/*      */     try {
/* 4273 */       InputStream bais = new SharedByteArrayInputStream(digestBlob);
/* 4274 */       Mime.FixedMimeMessage digestMm = new Mime.FixedMimeMessage(JMSession.getSession(), bais);
/*      */       
/*      */ 
/*      */ 
/* 4278 */       Object obj = digestMm.getContent();
/* 4279 */       MimeMultipart mmp; if ((obj instanceof MimeMultipart)) {
/* 4280 */         mmp = (MimeMultipart)obj;
/*      */       } else {
/* 4282 */         throw ServiceException.FAILURE("Expected MimeMultipart, but got " + obj.getClass().getName() + ": " + obj.toString(), null);
/*      */       }
/*      */       
/*      */       MimeMultipart mmp;
/* 4286 */       int numParts = mmp.getCount();
/* 4287 */       for (int i = 0; i < numParts; i++) {
/* 4288 */         MimeBodyPart mbp = (MimeBodyPart)mmp.getBodyPart(i);
/* 4289 */         int invId = 0;
/* 4290 */         String[] hdrs = mbp.getHeader("invId");
/* 4291 */         if ((hdrs != null) && (hdrs.length > 0)) {
/* 4292 */           invId = Integer.parseInt(hdrs[0]);
/*      */           
/* 4294 */           Object objMbp = mbp.getContent();
/* 4295 */           MimeMessage mm; if ((objMbp instanceof MimeMessage)) {
/* 4296 */             mm = (MimeMessage)objMbp;
/*      */           } else {
/* 4298 */             throw ServiceException.FAILURE("Expected MimeMessage, but got " + objMbp.getClass().getName() + ": " + objMbp.toString(), null);
/*      */           }
/*      */           MimeMessage mm;
/* 4301 */           map.put(Integer.valueOf(invId), mm);
/*      */         }
/*      */       }
/*      */     } catch (MessagingException e) {
/* 4305 */       throw ServiceException.FAILURE("Can't parse calendar item blob", e);
/*      */     } catch (IOException e) {
/* 4307 */       throw ServiceException.FAILURE("Can't parse calendar item blob", e);
/*      */     } catch (NumberFormatException e) {
/* 4309 */       throw ServiceException.FAILURE("Can't parse calendar item blob", e);
/*      */     }
/* 4311 */     return map;
/*      */   }
/*      */   
/*      */   public static boolean isAcceptableInvite(Account acct, ParsedMessage.CalendarPartInfo cpi) {
/* 4315 */     if ((cpi.wasForwarded) && (!acct.getBooleanAttr("zimbraPrefCalendarAllowForwardedInvite", true)))
/*      */     {
/* 4317 */       return false; }
/* 4318 */     return true;
/*      */   }
/*      */   
/*      */   int getMaxRevisions() throws ServiceException
/*      */   {
/* 4323 */     return getAccount().getIntAttr("zimbraCalendarMaxRevisions", 1);
/*      */   }
/*      */   
/*      */   public void snapshotRevision() throws ServiceException {
/* 4327 */     snapshotRevision(true);
/*      */   }
/*      */   
/*      */   public void snapshotRevision(boolean updateFolderMODSEQ) throws ServiceException {
/* 4331 */     addRevision(false, updateFolderMODSEQ);
/*      */   }
/*      */   
/*      */   protected boolean trackUserAgentInMetadata()
/*      */   {
/* 4336 */     return true;
/*      */   }
/*      */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/mailbox/CalendarItem.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */